diff --git a/v2/array/any.go b/v2/array/any.go new file mode 100644 index 0000000..eece62c --- /dev/null +++ b/v2/array/any.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + G "github.com/IBM/fp-go/v2/array/generic" +) + +// AnyWithIndex tests if any of the elements in the array matches the predicate +func AnyWithIndex[A any](pred func(int, A) bool) func([]A) bool { + return G.AnyWithIndex[[]A](pred) +} + +// Any tests if any of the elements in the array matches the predicate +func Any[A any](pred func(A) bool) func([]A) bool { + return G.Any[[]A](pred) +} diff --git a/v2/array/any_test.go b/v2/array/any_test.go new file mode 100644 index 0000000..d80ff12 --- /dev/null +++ b/v2/array/any_test.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestAny(t *testing.T) { + anyBool := Any(F.Identity[bool]) + + assert.True(t, anyBool(From(false, true, false))) + assert.False(t, anyBool(From(false, false, false))) +} diff --git a/v2/array/array.go b/v2/array/array.go new file mode 100644 index 0000000..b7fdcbf --- /dev/null +++ b/v2/array/array.go @@ -0,0 +1,351 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + G "github.com/IBM/fp-go/v2/array/generic" + EM "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/array" + M "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/tuple" +) + +// From constructs an array from a set of variadic arguments +func From[A any](data ...A) []A { + return G.From[[]A](data...) +} + +// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`. +func MakeBy[F ~func(int) A, A any](n int, f F) []A { + return G.MakeBy[[]A](n, f) +} + +// Replicate creates a `Array` containing a value repeated the specified number of times. +func Replicate[A any](n int, a A) []A { + return G.Replicate[[]A](n, a) +} + +func MonadMap[A, B any](as []A, f func(a A) B) []B { + return G.MonadMap[[]A, []B](as, f) +} + +func MonadMapRef[A, B any](as []A, f func(a *A) B) []B { + count := len(as) + bs := make([]B, count) + for i := count - 1; i >= 0; i-- { + bs[i] = f(&as[i]) + } + return bs +} + +func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B { + return G.MapWithIndex[[]A, []B](f) +} + +func Map[A, B any](f func(a A) B) func([]A) []B { + return G.Map[[]A, []B, A, B](f) +} + +func MapRef[A, B any](f func(a *A) B) func([]A) []B { + return F.Bind2nd(MonadMapRef[A, B], f) +} + +func filterRef[A any](fa []A, pred func(a *A) bool) []A { + var result []A + count := len(fa) + for i := 0; i < count; i++ { + a := fa[i] + if pred(&a) { + result = append(result, a) + } + } + return result +} + +func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B { + var result []B + count := len(fa) + for i := 0; i < count; i++ { + a := fa[i] + if pred(&a) { + result = append(result, f(&a)) + } + } + return result +} + +// Filter returns a new array with all elements from the original array that match a predicate +func Filter[A any](pred func(A) bool) EM.Endomorphism[[]A] { + return G.Filter[[]A](pred) +} + +// FilterWithIndex returns a new array with all elements from the original array that match a predicate +func FilterWithIndex[A any](pred func(int, A) bool) EM.Endomorphism[[]A] { + return G.FilterWithIndex[[]A](pred) +} + +func FilterRef[A any](pred func(*A) bool) EM.Endomorphism[[]A] { + return F.Bind2nd(filterRef[A], pred) +} + +func MonadFilterMap[A, B any](fa []A, f func(A) O.Option[B]) []B { + return G.MonadFilterMap[[]A, []B](fa, f) +} + +func MonadFilterMapWithIndex[A, B any](fa []A, f func(int, A) O.Option[B]) []B { + return G.MonadFilterMapWithIndex[[]A, []B](fa, f) +} + +// FilterMap maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones. +func FilterMap[A, B any](f func(A) O.Option[B]) func([]A) []B { + return G.FilterMap[[]A, []B](f) +} + +// FilterMapWithIndex maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones. +func FilterMapWithIndex[A, B any](f func(int, A) O.Option[B]) func([]A) []B { + return G.FilterMapWithIndex[[]A, []B](f) +} + +// FilterChain maps an array with an iterating function that returns an [O.Option] of an array. It keeps only the Some values discarding the Nones and then flattens the result. +func FilterChain[A, B any](f func(A) O.Option[[]B]) func([]A) []B { + return G.FilterChain[[]A](f) +} + +func FilterMapRef[A, B any](pred func(a *A) bool, f func(a *A) B) func([]A) []B { + return func(fa []A) []B { + return filterMapRef(fa, pred, f) + } +} + +func reduceRef[A, B any](fa []A, f func(B, *A) B, initial B) B { + current := initial + count := len(fa) + for i := 0; i < count; i++ { + current = f(current, &fa[i]) + } + return current +} + +func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B { + return G.Reduce[[]A](f, initial) +} + +func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func([]A) B { + return G.ReduceWithIndex[[]A](f, initial) +} + +func ReduceRight[A, B any](f func(A, B) B, initial B) func([]A) B { + return G.ReduceRight[[]A](f, initial) +} + +func ReduceRightWithIndex[A, B any](f func(int, A, B) B, initial B) func([]A) B { + return G.ReduceRightWithIndex[[]A](f, initial) +} + +func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B { + return func(as []A) B { + return reduceRef(as, f, initial) + } +} + +func Append[A any](as []A, a A) []A { + return G.Append(as, a) +} + +func IsEmpty[A any](as []A) bool { + return G.IsEmpty(as) +} + +func IsNonEmpty[A any](as []A) bool { + return len(as) > 0 +} + +func Empty[A any]() []A { + return G.Empty[[]A]() +} + +func Zero[A any]() []A { + return Empty[A]() +} + +// Of constructs a single element array +func Of[A any](a A) []A { + return G.Of[[]A](a) +} + +func MonadChain[A, B any](fa []A, f func(a A) []B) []B { + return G.MonadChain[[]A, []B](fa, f) +} + +func Chain[A, B any](f func(A) []B) func([]A) []B { + return G.Chain[[]A, []B](f) +} + +func MonadAp[B, A any](fab []func(A) B, fa []A) []B { + return G.MonadAp[[]B](fab, fa) +} + +func Ap[B, A any](fa []A) func([]func(A) B) []B { + return G.Ap[[]B, []func(A) B](fa) +} + +func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B { + return G.Match[[]A](onEmpty, onNonEmpty) +} + +func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B { + return G.MatchLeft[[]A](onEmpty, onNonEmpty) +} + +func Tail[A any](as []A) O.Option[[]A] { + return G.Tail(as) +} + +func Head[A any](as []A) O.Option[A] { + return G.Head(as) +} + +func First[A any](as []A) O.Option[A] { + return G.First(as) +} + +func Last[A any](as []A) O.Option[A] { + return G.Last(as) +} + +func PrependAll[A any](middle A) EM.Endomorphism[[]A] { + return func(as []A) []A { + count := len(as) + dst := count * 2 + result := make([]A, dst) + for i := count - 1; i >= 0; i-- { + dst-- + result[dst] = as[i] + dst-- + result[dst] = middle + } + return result + } +} + +func Intersperse[A any](middle A) EM.Endomorphism[[]A] { + prepend := PrependAll(middle) + return func(as []A) []A { + if IsEmpty(as) { + return as + } + return prepend(as)[1:] + } +} + +func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A { + concatAll := ConcatAll[A](m) + return func(middle A) func([]A) A { + return Match(m.Empty, F.Flow2(Intersperse(middle), concatAll)) + } +} + +func Flatten[A any](mma [][]A) []A { + return G.Flatten(mma) +} + +func Slice[A any](low, high int) func(as []A) []A { + return array.Slice[[]A](low, high) +} + +func Lookup[A any](idx int) func([]A) O.Option[A] { + return G.Lookup[[]A](idx) +} + +func UpsertAt[A any](a A) EM.Endomorphism[[]A] { + return G.UpsertAt[[]A](a) +} + +func Size[A any](as []A) int { + return G.Size(as) +} + +func MonadPartition[A any](as []A, pred func(A) bool) tuple.Tuple2[[]A, []A] { + return G.MonadPartition(as, pred) +} + +// Partition creates two new arrays out of one, the left result contains the elements +// for which the predicate returns false, the right one those for which the predicate returns true +func Partition[A any](pred func(A) bool) func([]A) tuple.Tuple2[[]A, []A] { + return G.Partition[[]A](pred) +} + +// IsNil checks if the array is set to nil +func IsNil[A any](as []A) bool { + return array.IsNil(as) +} + +// IsNonNil checks if the array is set to nil +func IsNonNil[A any](as []A) bool { + return array.IsNonNil(as) +} + +// ConstNil returns a nil array +func ConstNil[A any]() []A { + return array.ConstNil[[]A]() +} + +func SliceRight[A any](start int) EM.Endomorphism[[]A] { + return G.SliceRight[[]A](start) +} + +// Copy creates a shallow copy of the array +func Copy[A any](b []A) []A { + return G.Copy(b) +} + +// Clone creates a deep copy of the array using the provided endomorphism to clone the values +func Clone[A any](f func(A) A) func(as []A) []A { + return G.Clone[[]A](f) +} + +// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid. +func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B { + return G.FoldMap[[]A](m) +} + +// FoldMapWithIndex maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid. +func FoldMapWithIndex[A, B any](m M.Monoid[B]) func(func(int, A) B) func([]A) B { + return G.FoldMapWithIndex[[]A](m) +} + +// Fold folds the array using the provided Monoid. +func Fold[A any](m M.Monoid[A]) func([]A) A { + return G.Fold[[]A](m) +} + +func Push[A any](a A) EM.Endomorphism[[]A] { + return G.Push[EM.Endomorphism[[]A]](a) +} + +func MonadFlap[B, A any](fab []func(A) B, a A) []B { + return G.MonadFlap[func(A) B, []func(A) B, []B, A, B](fab, a) +} + +func Flap[B, A any](a A) func([]func(A) B) []B { + return G.Flap[func(A) B, []func(A) B, []B, A, B](a) +} + +func Prepend[A any](head A) EM.Endomorphism[[]A] { + return G.Prepend[EM.Endomorphism[[]A]](head) +} diff --git a/v2/array/array_test.go b/v2/array/array_test.go new file mode 100644 index 0000000..d9ac217 --- /dev/null +++ b/v2/array/array_test.go @@ -0,0 +1,205 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "fmt" + "strings" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + O "github.com/IBM/fp-go/v2/option" + S "github.com/IBM/fp-go/v2/string" + T "github.com/IBM/fp-go/v2/tuple" + "github.com/stretchr/testify/assert" +) + +func TestMap1(t *testing.T) { + + src := []string{"a", "b", "c"} + + up := Map(strings.ToUpper)(src) + + var up1 = []string{} + for _, s := range src { + up1 = append(up1, strings.ToUpper(s)) + } + + var up2 = []string{} + for i := range src { + up2 = append(up2, strings.ToUpper(src[i])) + } + + assert.Equal(t, up, up1) + assert.Equal(t, up, up2) +} + +func TestMap(t *testing.T) { + + mapper := Map(utils.Upper) + + src := []string{"a", "b", "c"} + + dst := mapper(src) + + assert.Equal(t, dst, []string{"A", "B", "C"}) +} + +func TestReduceRight(t *testing.T) { + values := From("a", "b", "c") + f := func(a, acc string) string { + return fmt.Sprintf("%s%s", acc, a) + } + b := "" + + assert.Equal(t, "cba", ReduceRight(f, b)(values)) + assert.Equal(t, "", ReduceRight(f, b)(Empty[string]())) +} + +func TestReduce(t *testing.T) { + + values := MakeBy(101, F.Identity[int]) + + sum := func(val int, current int) int { + return val + current + } + reducer := Reduce(sum, 0) + + result := reducer(values) + assert.Equal(t, result, 5050) + +} + +func TestEmpty(t *testing.T) { + assert.True(t, IsNonEmpty(MakeBy(101, F.Identity[int]))) + assert.True(t, IsEmpty([]int{})) +} + +func TestAp(t *testing.T) { + assert.Equal(t, + []int{2, 4, 6, 3, 6, 9}, + F.Pipe1( + []func(int) int{ + utils.Double, + utils.Triple, + }, + Ap[int, int]([]int{1, 2, 3}), + ), + ) +} + +func TestIntercalate(t *testing.T) { + is := Intercalate(S.Monoid)("-") + + assert.Equal(t, "", is(Empty[string]())) + assert.Equal(t, "a", is([]string{"a"})) + assert.Equal(t, "a-b-c", is([]string{"a", "b", "c"})) + assert.Equal(t, "a--c", is([]string{"a", "", "c"})) + assert.Equal(t, "a-b", is([]string{"a", "b"})) + assert.Equal(t, "a-b-c-d", is([]string{"a", "b", "c", "d"})) +} + +func TestPrependAll(t *testing.T) { + empty := Empty[int]() + prep := PrependAll(0) + assert.Equal(t, empty, prep(empty)) + assert.Equal(t, []int{0, 1, 0, 2, 0, 3}, prep([]int{1, 2, 3})) + assert.Equal(t, []int{0, 1}, prep([]int{1})) + assert.Equal(t, []int{0, 1, 0, 2, 0, 3, 0, 4}, prep([]int{1, 2, 3, 4})) +} + +func TestFlatten(t *testing.T) { + assert.Equal(t, []int{1, 2, 3}, Flatten([][]int{{1}, {2}, {3}})) +} + +func TestLookup(t *testing.T) { + data := []int{0, 1, 2} + none := O.None[int]() + + assert.Equal(t, none, Lookup[int](-1)(data)) + assert.Equal(t, none, Lookup[int](10)(data)) + assert.Equal(t, O.Some(1), Lookup[int](1)(data)) +} + +func TestSlice(t *testing.T) { + data := []int{0, 1, 2, 3} + + assert.Equal(t, []int{1, 2}, Slice[int](1, 3)(data)) +} + +func TestFrom(t *testing.T) { + assert.Equal(t, []int{1, 2, 3}, From(1, 2, 3)) +} + +func TestPartition(t *testing.T) { + + pred := func(n int) bool { + return n > 2 + } + + assert.Equal(t, T.MakeTuple2(Empty[int](), Empty[int]()), Partition(pred)(Empty[int]())) + assert.Equal(t, T.MakeTuple2(From(1), From(3)), Partition(pred)(From(1, 3))) +} + +func TestFilterChain(t *testing.T) { + src := From(1, 2, 3) + + f := func(i int) O.Option[[]string] { + if i%2 != 0 { + return O.Of(From(fmt.Sprintf("a%d", i), fmt.Sprintf("b%d", i))) + } + return O.None[[]string]() + } + + res := FilterChain(f)(src) + + assert.Equal(t, From("a1", "b1", "a3", "b3"), res) +} + +func TestFilterMap(t *testing.T) { + src := From(1, 2, 3) + + f := func(i int) O.Option[string] { + if i%2 != 0 { + return O.Of(fmt.Sprintf("a%d", i)) + } + return O.None[string]() + } + + res := FilterMap(f)(src) + + assert.Equal(t, From("a1", "a3"), res) +} + +func TestFoldMap(t *testing.T) { + src := From("a", "b", "c") + + fold := FoldMap[string](S.Monoid)(strings.ToUpper) + + assert.Equal(t, "ABC", fold(src)) +} + +func ExampleFoldMap() { + src := From("a", "b", "c") + + fold := FoldMap[string](S.Monoid)(strings.ToUpper) + + fmt.Println(fold(src)) + + // Output: ABC + +} diff --git a/v2/array/bind.go b/v2/array/bind.go new file mode 100644 index 0000000..96b0a3e --- /dev/null +++ b/v2/array/bind.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + G "github.com/IBM/fp-go/v2/array/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) []S { + return G.Do[[]S, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) []T, +) func([]S1) []S2 { + return G.Bind[[]S1, []S2, []T, S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func([]S1) []S2 { + return G.Let[[]S1, []S2, S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func([]S1) []S2 { + return G.LetTo[[]S1, []S2, S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func([]T) []S1 { + return G.BindTo[[]S1, []T, S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa []T, +) func([]S1) []S2 { + return G.ApS[[]S1, []S2, []T, S1, S2, T](setter, fa) +} diff --git a/v2/array/bind_test.go b/v2/array/bind_test.go new file mode 100644 index 0000000..38a9415 --- /dev/null +++ b/v2/array/bind_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) []string { + return Of("Doe") +} + +func getGivenName(s utils.WithLastName) []string { + return Of("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map(utils.GetFullName), + ) + + assert.Equal(t, res, Of("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + ApS(utils.SetLastName, Of("Doe")), + ApS(utils.SetGivenName, Of("John")), + Map(utils.GetFullName), + ) + + assert.Equal(t, res, Of("John Doe")) +} diff --git a/v2/array/eq.go b/v2/array/eq.go new file mode 100644 index 0000000..12e3f47 --- /dev/null +++ b/v2/array/eq.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + E "github.com/IBM/fp-go/v2/eq" +) + +func equals[T any](left []T, right []T, eq func(T, T) bool) bool { + if len(left) != len(right) { + return false + } + for i, v1 := range left { + v2 := right[i] + if !eq(v1, v2) { + return false + } + } + return true +} + +func Eq[T any](e E.Eq[T]) E.Eq[[]T] { + eq := e.Equals + return E.FromEquals(func(left, right []T) bool { + return equals(left, right, eq) + }) +} diff --git a/v2/array/example_any_test.go b/v2/array/example_any_test.go new file mode 100644 index 0000000..f06e2d0 --- /dev/null +++ b/v2/array/example_any_test.go @@ -0,0 +1,77 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +func Example_any() { + + pred := func(val int) bool { + return val&2 == 0 + } + + data1 := From(1, 2, 3) + + fmt.Println(Any(pred)(data1)) + + // Output: + // true +} + +func Example_any_filter() { + + pred := func(val int) bool { + return val&2 == 0 + } + + data1 := From(1, 2, 3) + + // Any tests if any of the entries in the array matches the condition + Any := F.Flow2( + Filter(pred), + IsNonEmpty[int], + ) + + fmt.Println(Any(data1)) + + // Output: + // true +} + +func Example_any_find() { + + pred := func(val int) bool { + return val&2 == 0 + } + + data1 := From(1, 2, 3) + + // Any tests if any of the entries in the array matches the condition + Any := F.Flow2( + FindFirst(pred), + O.IsSome[int], + ) + + fmt.Println(Any(data1)) + + // Output: + // true +} diff --git a/v2/array/example_find_test.go b/v2/array/example_find_test.go new file mode 100644 index 0000000..7ec2a14 --- /dev/null +++ b/v2/array/example_find_test.go @@ -0,0 +1,55 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" +) + +func Example_find() { + + pred := func(val int) bool { + return val&2 == 0 + } + + data1 := From(1, 2, 3) + + fmt.Println(FindFirst(pred)(data1)) + + // Output: + // Some[int](1) +} + +func Example_find_filter() { + + pred := func(val int) bool { + return val&2 == 0 + } + + data1 := From(1, 2, 3) + + Find := F.Flow2( + Filter(pred), + Head[int], + ) + + fmt.Println(Find(data1)) + + // Output: + // Some[int](1) +} diff --git a/v2/array/examples_basic_test.go b/v2/array/examples_basic_test.go new file mode 100644 index 0000000..b490826 --- /dev/null +++ b/v2/array/examples_basic_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +// Example_basic adapts examples from [https://github.com/inato/fp-ts-cheatsheet#basic-manipulation] +func Example_basic() { + + someArray := From(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) // []int + + isEven := func(num int) bool { + return num%2 == 0 + } + + square := func(num int) int { + return num * num + } + + // filter and map + result := F.Pipe2( + someArray, + Filter(isEven), + Map(square), + ) // [0 4 16 36 64] + + // or in one go with filterMap + resultFilterMap := F.Pipe1( + someArray, + FilterMap( + F.Flow2(O.FromPredicate(isEven), O.Map(square)), + ), + ) + + fmt.Println(result) + fmt.Println(resultFilterMap) + + // Output: + // [0 4 16 36 64] + // [0 4 16 36 64] +} diff --git a/v2/array/examples_sort_test.go b/v2/array/examples_sort_test.go new file mode 100644 index 0000000..6a1908b --- /dev/null +++ b/v2/array/examples_sort_test.go @@ -0,0 +1,92 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/number/integer" + O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/ord" + S "github.com/IBM/fp-go/v2/string" +) + +type user struct { + name string + age O.Option[int] +} + +func (user user) GetName() string { + return user.name +} + +func (user user) GetAge() O.Option[int] { + return user.age +} + +// Example_sort adapts examples from [https://github.com/inato/fp-ts-cheatsheet#sort-elements-with-ord] +func Example_sort() { + + strings := From("zyx", "abc", "klm") + + sortedStrings := F.Pipe1( + strings, + Sort(S.Ord), + ) // => ['abc', 'klm', 'zyx'] + + // reverse sort + reverseSortedStrings := F.Pipe1( + strings, + Sort(ord.Reverse(S.Ord)), + ) // => ['zyx', 'klm', 'abc'] + + // sort Option + optionalNumbers := From(O.Some(1337), O.None[int](), O.Some(42)) + + sortedNums := F.Pipe1( + optionalNumbers, + Sort(O.Ord(I.Ord)), + ) + + // complex object with different rules + byName := F.Pipe1( + S.Ord, + ord.Contramap(user.GetName), + ) // ord.Ord[user] + + byAge := F.Pipe1( + O.Ord(I.Ord), + ord.Contramap(user.GetAge), + ) // ord.Ord[user] + + sortedUsers := F.Pipe1( + From(user{name: "a", age: O.Of(30)}, user{name: "d", age: O.Of(10)}, user{name: "c"}, user{name: "b", age: O.Of(10)}), + SortBy(From(byAge, byName)), + ) + + fmt.Println(sortedStrings) + fmt.Println(reverseSortedStrings) + fmt.Println(sortedNums) + fmt.Println(sortedUsers) + + // Output: + // [abc klm zyx] + // [zyx klm abc] + // [None[int] Some[int](42) Some[int](1337)] + // [{c {false 0}} {b {true 10}} {d {true 10}} {a {true 30}}] + +} diff --git a/v2/array/find.go b/v2/array/find.go new file mode 100644 index 0000000..89e1fc1 --- /dev/null +++ b/v2/array/find.go @@ -0,0 +1,61 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + G "github.com/IBM/fp-go/v2/array/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// FindFirst finds the first element which satisfies a predicate (or a refinement) function +func FindFirst[A any](pred func(A) bool) func([]A) O.Option[A] { + return G.FindFirst[[]A](pred) +} + +// FindFirstWithIndex finds the first element which satisfies a predicate (or a refinement) function +func FindFirstWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] { + return G.FindFirstWithIndex[[]A](pred) +} + +// FindFirstMap finds the first element returned by an [O.Option] based selector function +func FindFirstMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] { + return G.FindFirstMap[[]A](sel) +} + +// FindFirstMapWithIndex finds the first element returned by an [O.Option] based selector function +func FindFirstMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] { + return G.FindFirstMapWithIndex[[]A](sel) +} + +// FindLast finds the Last element which satisfies a predicate (or a refinement) function +func FindLast[A any](pred func(A) bool) func([]A) O.Option[A] { + return G.FindLast[[]A](pred) +} + +// FindLastWithIndex finds the Last element which satisfies a predicate (or a refinement) function +func FindLastWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] { + return G.FindLastWithIndex[[]A](pred) +} + +// FindLastMap finds the Last element returned by an [O.Option] based selector function +func FindLastMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] { + return G.FindLastMap[[]A](sel) +} + +// FindLastMapWithIndex finds the Last element returned by an [O.Option] based selector function +func FindLastMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] { + return G.FindLastMapWithIndex[[]A](sel) +} diff --git a/v2/array/generic/any.go b/v2/array/generic/any.go new file mode 100644 index 0000000..5405b24 --- /dev/null +++ b/v2/array/generic/any.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +// AnyWithIndex tests if any of the elements in the array matches the predicate +func AnyWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) bool { + return F.Flow2( + FindFirstWithIndex[AS](pred), + O.IsSome[A], + ) +} + +// Any tests if any of the elements in the array matches the predicate +func Any[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) bool { + return AnyWithIndex[AS](F.Ignore1of2[int](pred)) +} diff --git a/v2/array/generic/array.go b/v2/array/generic/array.go new file mode 100644 index 0000000..4021523 --- /dev/null +++ b/v2/array/generic/array.go @@ -0,0 +1,352 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/array" + FC "github.com/IBM/fp-go/v2/internal/functor" + M "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/tuple" +) + +// Of constructs a single element array +func Of[GA ~[]A, A any](value A) GA { + return GA{value} +} + +func Reduce[GA ~[]A, A, B any](f func(B, A) B, initial B) func(GA) B { + return func(as GA) B { + return MonadReduce[GA](as, f, initial) + } +} + +func ReduceWithIndex[GA ~[]A, A, B any](f func(int, B, A) B, initial B) func(GA) B { + return func(as GA) B { + return MonadReduceWithIndex[GA](as, f, initial) + } +} + +func ReduceRight[GA ~[]A, A, B any](f func(A, B) B, initial B) func(GA) B { + return func(as GA) B { + return MonadReduceRight[GA](as, f, initial) + } +} + +func ReduceRightWithIndex[GA ~[]A, A, B any](f func(int, A, B) B, initial B) func(GA) B { + return func(as GA) B { + return MonadReduceRightWithIndex[GA](as, f, initial) + } +} + +func MonadReduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B { + return array.Reduce(fa, f, initial) +} + +func MonadReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B { + return array.ReduceWithIndex(fa, f, initial) +} + +func MonadReduceRight[GA ~[]A, A, B any](fa GA, f func(A, B) B, initial B) B { + return array.ReduceRight(fa, f, initial) +} + +func MonadReduceRightWithIndex[GA ~[]A, A, B any](fa GA, f func(int, A, B) B, initial B) B { + return array.ReduceRightWithIndex(fa, f, initial) +} + +// From constructs an array from a set of variadic arguments +func From[GA ~[]A, A any](data ...A) GA { + return data +} + +// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`. +func MakeBy[AS ~[]A, F ~func(int) A, A any](n int, f F) AS { + // sanity check + if n <= 0 { + return Empty[AS]() + } + // run the generator function across the input + as := make(AS, n) + for i := n - 1; i >= 0; i-- { + as[i] = f(i) + } + return as +} + +func Replicate[AS ~[]A, A any](n int, a A) AS { + return MakeBy[AS](n, F.Constant1[int](a)) +} + +func Lookup[GA ~[]A, A any](idx int) func(GA) O.Option[A] { + none := O.None[A]() + if idx < 0 { + return F.Constant1[GA](none) + } + return func(as GA) O.Option[A] { + if idx < len(as) { + return O.Some(as[idx]) + } + return none + } +} + +func Tail[GA ~[]A, A any](as GA) O.Option[GA] { + if array.IsEmpty(as) { + return O.None[GA]() + } + return O.Some(as[1:]) +} + +func Head[GA ~[]A, A any](as GA) O.Option[A] { + if array.IsEmpty(as) { + return O.None[A]() + } + return O.Some(as[0]) +} + +func First[GA ~[]A, A any](as GA) O.Option[A] { + return Head(as) +} + +func Last[GA ~[]A, A any](as GA) O.Option[A] { + if array.IsEmpty(as) { + return O.None[A]() + } + return O.Some(as[len(as)-1]) +} + +func Append[GA ~[]A, A any](as GA, a A) GA { + return array.Append(as, a) +} + +func Empty[GA ~[]A, A any]() GA { + return array.Empty[GA]() +} + +func UpsertAt[GA ~[]A, A any](a A) func(GA) GA { + return array.UpsertAt[GA](a) +} + +func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB { + return array.MonadMap[GA, GB](as, f) +} + +func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB { + return array.Map[GA, GB](f) +} + +func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(int, A) B) GB { + return array.MonadMapWithIndex[GA, GB](as, f) +} + +func MapWithIndex[GA ~[]A, GB ~[]B, A, B any](f func(int, A) B) func(GA) GB { + return F.Bind2nd(MonadMapWithIndex[GA, GB, A, B], f) +} + +func Size[GA ~[]A, A any](as GA) int { + return len(as) +} + +func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB { + return array.Reduce(fa, func(bs GB, a A) GB { + return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs)) + }, Empty[GB]()) +} + +func filterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB { + return array.ReduceWithIndex(fa, func(idx int, bs GB, a A) GB { + return O.MonadFold(f(idx, a), F.Constant(bs), F.Bind1st(Append[GB, B], bs)) + }, Empty[GB]()) +} + +func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB { + return filterMap[GA, GB](fa, f) +} + +func MonadFilterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB { + return filterMapWithIndex[GA, GB](fa, f) +} + +func filterWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](fa AS, pred PRED) AS { + result := make(AS, 0, len(fa)) + for i, a := range fa { + if pred(i, a) { + result = append(result, a) + } + } + return result +} + +func FilterWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) AS { + return F.Bind2nd(filterWithIndex[AS, PRED, A], pred) +} + +func Filter[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) AS { + return FilterWithIndex[AS](F.Ignore1of2[int](pred)) +} + +func FilterChain[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[GB]) func(GA) GB { + return F.Flow2( + FilterMap[GA, []GB](f), + Flatten[[]GB], + ) +} + +func Flatten[GAA ~[]GA, GA ~[]A, A any](mma GAA) GA { + return MonadChain(mma, F.Identity[GA]) +} + +func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(A) O.Option[B]) func(GA) GB { + return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f) +} + +func FilterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](f func(int, A) O.Option[B]) func(GA) GB { + return F.Bind2nd(MonadFilterMapWithIndex[GA, GB, A, B], f) +} + +func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] { + left := Empty[GA]() + right := Empty[GA]() + array.Reduce(as, func(c bool, a A) bool { + if pred(a) { + right = append(right, a) + } else { + left = append(left, a) + } + return c + }, true) + // returns the partition + return tuple.MakeTuple2(left, right) +} + +func Partition[GA ~[]A, A any](pred func(A) bool) func(GA) tuple.Tuple2[GA, GA] { + return F.Bind2nd(MonadPartition[GA, A], pred) +} + +func MonadChain[AS ~[]A, BS ~[]B, A, B any](fa AS, f func(a A) BS) BS { + return array.Reduce(fa, func(bs BS, a A) BS { + return append(bs, f(a)...) + }, Empty[BS]()) +} + +func Chain[AS ~[]A, BS ~[]B, A, B any](f func(A) BS) func(AS) BS { + return F.Bind2nd(MonadChain[AS, BS, A, B], f) +} + +func MonadAp[BS ~[]B, ABS ~[]func(A) B, AS ~[]A, B, A any](fab ABS, fa AS) BS { + return MonadChain(fab, F.Bind1st(MonadMap[AS, BS, A, B], fa)) +} + +func Ap[BS ~[]B, ABS ~[]func(A) B, AS ~[]A, B, A any](fa AS) func(ABS) BS { + return F.Bind2nd(MonadAp[BS, ABS, AS], fa) +} + +func IsEmpty[AS ~[]A, A any](as AS) bool { + return array.IsEmpty(as) +} + +func IsNil[GA ~[]A, A any](as GA) bool { + return array.IsNil(as) +} + +func IsNonNil[GA ~[]A, A any](as GA) bool { + return array.IsNonNil(as) +} + +func Match[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(AS) B) func(AS) B { + return func(as AS) B { + if IsEmpty(as) { + return onEmpty() + } + return onNonEmpty(as) + } +} + +func MatchLeft[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(A, AS) B) func(AS) B { + return func(as AS) B { + if IsEmpty(as) { + return onEmpty() + } + return onNonEmpty(as[0], as[1:]) + } +} + +func Slice[AS ~[]A, A any](start int, end int) func(AS) AS { + return func(a AS) AS { + return a[start:end] + } +} + +func SliceRight[AS ~[]A, A any](start int) func(AS) AS { + return func(a AS) AS { + return a[start:] + } +} + +func Copy[AS ~[]A, A any](b AS) AS { + buf := make(AS, len(b)) + copy(buf, b) + return buf +} + +func Clone[AS ~[]A, A any](f func(A) A) func(as AS) AS { + // implementation assumes that map does not optimize for the empty array + return Map[AS, AS](f) +} + +func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B { + return func(f func(A) B) func(AS) B { + return func(as AS) B { + return array.Reduce(as, func(cur B, a A) B { + return m.Concat(cur, f(a)) + }, m.Empty()) + } + } +} + +func FoldMapWithIndex[AS ~[]A, A, B any](m M.Monoid[B]) func(func(int, A) B) func(AS) B { + return func(f func(int, A) B) func(AS) B { + return func(as AS) B { + return array.ReduceWithIndex(as, func(idx int, cur B, a A) B { + return m.Concat(cur, f(idx, a)) + }, m.Empty()) + } + } +} + +func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A { + return func(as AS) A { + return array.Reduce(as, m.Concat, m.Empty()) + } +} + +func Push[ENDO ~func(GA) GA, GA ~[]A, A any](a A) ENDO { + return F.Bind2nd(array.Push[GA, A], a) +} + +func MonadFlap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](fab GFAB, a A) GB { + return FC.MonadFlap(MonadMap[GFAB, GB], fab, a) +} + +func Flap[FAB ~func(A) B, GFAB ~[]FAB, GB ~[]B, A, B any](a A) func(GFAB) GB { + return FC.Flap(Map[GFAB, GB], a) +} + +func Prepend[ENDO ~func(AS) AS, AS []A, A any](head A) ENDO { + return array.Prepend[ENDO](head) +} diff --git a/v2/array/generic/bind.go b/v2/array/generic/bind.go new file mode 100644 index 0000000..14cd3e6 --- /dev/null +++ b/v2/array/generic/bind.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GS ~[]S, S any]( + empty S, +) GS { + return Of[GS](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GS1 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GT, +) func(GS1) GS2 { + return C.Bind( + Chain[GS1, GS2, S1, S2], + Map[GT, GS2, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GS1 ~[]S1, GS2 ~[]S2, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GS1) GS2 { + return F.Let( + Map[GS1, GS2, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GS1 ~[]S1, GS2 ~[]S2, S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GS1) GS2 { + return F.LetTo( + Map[GS1, GS2, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GS1 ~[]S1, GT ~[]T, S1, T any]( + setter func(T) S1, +) func(GT) GS1 { + return C.BindTo( + Map[GT, GS1, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GS1 ~[]S1, GS2 ~[]S2, GT ~[]T, S1, S2, T any]( + setter func(T) func(S1) S2, + fa GT, +) func(GS1) GS2 { + return A.ApS( + Ap[GS2, []func(T) S2, GT, S2, T], + Map[GS1, []func(T) S2, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/array/generic/find.go b/v2/array/generic/find.go new file mode 100644 index 0000000..5ea487b --- /dev/null +++ b/v2/array/generic/find.go @@ -0,0 +1,97 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +// FindFirstWithIndex finds the first element which satisfies a predicate (or a refinement) function +func FindFirstWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) O.Option[A] { + none := O.None[A]() + return func(as AS) O.Option[A] { + for i, a := range as { + if pred(i, a) { + return O.Some(a) + } + } + return none + } +} + +// FindFirst finds the first element which satisfies a predicate (or a refinement) function +func FindFirst[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[A] { + return FindFirstWithIndex[AS](F.Ignore1of2[int](pred)) +} + +// FindFirstMapWithIndex finds the first element returned by an [O.Option] based selector function +func FindFirstMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] { + none := O.None[B]() + return func(as AS) O.Option[B] { + count := len(as) + for i := 0; i < count; i++ { + out := pred(i, as[i]) + if O.IsSome(out) { + return out + } + } + return none + } +} + +// FindFirstMap finds the first element returned by an [O.Option] based selector function +func FindFirstMap[AS ~[]A, PRED ~func(A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] { + return FindFirstMapWithIndex[AS](F.Ignore1of2[int](pred)) +} + +// FindLastWithIndex finds the first element which satisfies a predicate (or a refinement) function +func FindLastWithIndex[AS ~[]A, PRED ~func(int, A) bool, A any](pred PRED) func(AS) O.Option[A] { + none := O.None[A]() + return func(as AS) O.Option[A] { + for i := len(as) - 1; i >= 0; i-- { + a := as[i] + if pred(i, a) { + return O.Some(a) + } + } + return none + } +} + +// FindLast finds the first element which satisfies a predicate (or a refinement) function +func FindLast[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[A] { + return FindLastWithIndex[AS](F.Ignore1of2[int](pred)) +} + +// FindLastMapWithIndex finds the first element returned by an [O.Option] based selector function +func FindLastMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] { + none := O.None[B]() + return func(as AS) O.Option[B] { + for i := len(as) - 1; i >= 0; i-- { + out := pred(i, as[i]) + if O.IsSome(out) { + return out + } + } + return none + } +} + +// FindLastMap finds the first element returned by an [O.Option] based selector function +func FindLastMap[AS ~[]A, PRED ~func(A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] { + return FindLastMapWithIndex[AS](F.Ignore1of2[int](pred)) +} diff --git a/v2/array/generic/monad.go b/v2/array/generic/monad.go new file mode 100644 index 0000000..de4da51 --- /dev/null +++ b/v2/array/generic/monad.go @@ -0,0 +1,43 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/monad" +) + +type arrayMonad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B] struct{} + +func (o *arrayMonad[A, B, GA, GB, GAB]) Of(a A) GA { + return Of[GA, A](a) +} + +func (o *arrayMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB { + return Map[GA, GB, A, B](f) +} + +func (o *arrayMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB { + return Chain[GA, GB, A, B](f) +} + +func (o *arrayMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB { + return Ap[GB, GAB, GA, B, A](fa) +} + +// Monad implements the monadic operations for an array +func Monad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B]() monad.Monad[A, B, GA, GB, GAB] { + return &arrayMonad[A, B, GA, GB, GAB]{} +} diff --git a/v2/array/generic/sort.go b/v2/array/generic/sort.go new file mode 100644 index 0000000..7a07f53 --- /dev/null +++ b/v2/array/generic/sort.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "sort" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/ord" +) + +// Sort implements a stable sort on the array given the provided ordering +func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA { + return SortByKey[GA](ord, F.Identity[T]) +} + +// SortByKey implements a stable sort on the array given the provided ordering on an extracted key +func SortByKey[GA ~[]T, K, T any](ord O.Ord[K], f func(T) K) func(ma GA) GA { + + return func(ma GA) GA { + // nothing to sort + l := len(ma) + if l < 2 { + return ma + } + // copy + cpy := make(GA, l) + copy(cpy, ma) + sort.Slice(cpy, func(i, j int) bool { + return ord.Compare(f(cpy[i]), f(cpy[j])) < 0 + }) + return cpy + } +} + +// SortBy implements a stable sort on the array given the provided ordering +func SortBy[GA ~[]T, GO ~[]O.Ord[T], T any](ord GO) func(ma GA) GA { + return F.Pipe2( + ord, + Fold[GO](O.Monoid[T]()), + Sort[GA, T], + ) +} diff --git a/v2/array/generic/uniq.go b/v2/array/generic/uniq.go new file mode 100644 index 0000000..66330ae --- /dev/null +++ b/v2/array/generic/uniq.go @@ -0,0 +1,32 @@ +package generic + +import F "github.com/IBM/fp-go/v2/function" + +// StrictUniq converts an array of arbitrary items into an array or unique items +// where uniqueness is determined by the built-in uniqueness constraint +func StrictUniq[AS ~[]A, A comparable](as AS) AS { + return Uniq[AS](F.Identity[A])(as) +} + +// uniquePredUnsafe returns a predicate on a map for uniqueness +func uniquePredUnsafe[PRED ~func(A) K, A any, K comparable](f PRED) func(int, A) bool { + lookup := make(map[K]bool) + return func(_ int, a A) bool { + k := f(a) + _, has := lookup[k] + if has { + return false + } + lookup[k] = true + return true + } +} + +// Uniq converts an array of arbitrary items into an array or unique items +// where uniqueness is determined based on a key extractor function +func Uniq[AS ~[]A, PRED ~func(A) K, A any, K comparable](f PRED) func(as AS) AS { + return func(as AS) AS { + // we need to create a new predicate for each iteration + return filterWithIndex(as, uniquePredUnsafe(f)) + } +} diff --git a/v2/array/generic/zip.go b/v2/array/generic/zip.go new file mode 100644 index 0000000..4520e60 --- /dev/null +++ b/v2/array/generic/zip.go @@ -0,0 +1,52 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" + T "github.com/IBM/fp-go/v2/tuple" +) + +// ZipWith applies a function to pairs of elements at the same index in two arrays, collecting the results in a new array. If one +// input array is short, excess elements of the longer array are discarded. +func ZipWith[AS ~[]A, BS ~[]B, CS ~[]C, FCT ~func(A, B) C, A, B, C any](fa AS, fb BS, f FCT) CS { + l := N.Min(len(fa), len(fb)) + res := make(CS, l) + for i := l - 1; i >= 0; i-- { + res[i] = f(fa[i], fb[i]) + } + return res +} + +// Zip takes two arrays and returns an array of corresponding pairs. If one input array is short, excess elements of the +// longer array are discarded +func Zip[AS ~[]A, BS ~[]B, CS ~[]T.Tuple2[A, B], A, B any](fb BS) func(AS) CS { + return F.Bind23of3(ZipWith[AS, BS, CS, func(A, B) T.Tuple2[A, B]])(fb, T.MakeTuple2[A, B]) +} + +// Unzip is the function is reverse of [Zip]. Takes an array of pairs and return two corresponding arrays +func Unzip[AS ~[]A, BS ~[]B, CS ~[]T.Tuple2[A, B], A, B any](cs CS) T.Tuple2[AS, BS] { + l := len(cs) + as := make(AS, l) + bs := make(BS, l) + for i := l - 1; i >= 0; i-- { + t := cs[i] + as[i] = t.F1 + bs[i] = t.F2 + } + return T.MakeTuple2(as, bs) +} diff --git a/v2/array/magma.go b/v2/array/magma.go new file mode 100644 index 0000000..b538120 --- /dev/null +++ b/v2/array/magma.go @@ -0,0 +1,24 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + M "github.com/IBM/fp-go/v2/monoid" +) + +func ConcatAll[A any](m M.Monoid[A]) func([]A) A { + return Reduce(m.Concat, m.Empty()) +} diff --git a/v2/array/magma_test.go b/v2/array/magma_test.go new file mode 100644 index 0000000..fc79c52 --- /dev/null +++ b/v2/array/magma_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + M "github.com/IBM/fp-go/v2/monoid" +) + +var subInt = M.MakeMonoid(func(first int, second int) int { + return first - second +}, 0) + +func TestConcatAll(t *testing.T) { + + var subAll = ConcatAll(subInt) + + assert.Equal(t, subAll([]int{1, 2, 3}), -6) + +} diff --git a/v2/array/monad.go b/v2/array/monad.go new file mode 100644 index 0000000..aa4df8b --- /dev/null +++ b/v2/array/monad.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + G "github.com/IBM/fp-go/v2/array/generic" + "github.com/IBM/fp-go/v2/internal/monad" +) + +// Monad returns the monadic operations for an array +func Monad[A, B any]() monad.Monad[A, B, []A, []B, []func(A) B] { + return G.Monad[A, B, []A, []B, []func(A) B]() +} diff --git a/v2/array/monoid.go b/v2/array/monoid.go new file mode 100644 index 0000000..ab661a6 --- /dev/null +++ b/v2/array/monoid.go @@ -0,0 +1,63 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "github.com/IBM/fp-go/v2/internal/array" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func concat[T any](left, right []T) []T { + // some performance checks + ll := len(left) + if ll == 0 { + return right + } + lr := len(right) + if lr == 0 { + return left + } + // need to copy + buf := make([]T, ll+lr) + copy(buf[copy(buf, left):], right) + return buf +} + +func Monoid[T any]() M.Monoid[[]T] { + return M.MakeMonoid(concat[T], Empty[T]()) +} + +func Semigroup[T any]() S.Semigroup[[]T] { + return S.MakeSemigroup(concat[T]) +} + +func addLen[A any](count int, data []A) int { + return count + len(data) +} + +// ConcatAll efficiently concatenates the input arrays into a final array +func ArrayConcatAll[A any](data ...[]A) []A { + // get the full size + count := array.Reduce(data, addLen[A], 0) + buf := make([]A, count) + // copy + array.Reduce(data, func(idx int, seg []A) int { + return idx + copy(buf[idx:], seg) + }, 0) + // returns the final array + return buf +} diff --git a/v2/array/monoid_test.go b/v2/array/monoid_test.go new file mode 100644 index 0000000..0bd8cf0 --- /dev/null +++ b/v2/array/monoid_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "testing" + + M "github.com/IBM/fp-go/v2/monoid/testing" +) + +func TestMonoid(t *testing.T) { + M.AssertLaws(t, Monoid[int]())([][]int{{}, {1}, {1, 2}}) +} diff --git a/v2/array/nonempty/array.go b/v2/array/nonempty/array.go new file mode 100644 index 0000000..a2067fd --- /dev/null +++ b/v2/array/nonempty/array.go @@ -0,0 +1,136 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nonempty + +import ( + G "github.com/IBM/fp-go/v2/array/generic" + EM "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/array" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// NonEmptyArray represents an array with at least one element +type NonEmptyArray[A any] []A + +// Of constructs a single element array +func Of[A any](first A) NonEmptyArray[A] { + return G.Of[NonEmptyArray[A]](first) +} + +// From constructs a [NonEmptyArray] from a set of variadic arguments +func From[A any](first A, data ...A) NonEmptyArray[A] { + count := len(data) + if count == 0 { + return Of(first) + } + // allocate the requested buffer + buffer := make(NonEmptyArray[A], count+1) + buffer[0] = first + copy(buffer[1:], data) + return buffer +} + +func IsEmpty[A any](_ NonEmptyArray[A]) bool { + return false +} + +func IsNonEmpty[A any](_ NonEmptyArray[A]) bool { + return true +} + +func MonadMap[A, B any](as NonEmptyArray[A], f func(a A) B) NonEmptyArray[B] { + return G.MonadMap[NonEmptyArray[A], NonEmptyArray[B]](as, f) +} + +func Map[A, B any](f func(a A) B) func(NonEmptyArray[A]) NonEmptyArray[B] { + return F.Bind2nd(MonadMap[A, B], f) +} + +func Reduce[A, B any](f func(B, A) B, initial B) func(NonEmptyArray[A]) B { + return func(as NonEmptyArray[A]) B { + return array.Reduce(as, f, initial) + } +} + +func ReduceRight[A, B any](f func(A, B) B, initial B) func(NonEmptyArray[A]) B { + return func(as NonEmptyArray[A]) B { + return array.ReduceRight(as, f, initial) + } +} + +func Tail[A any](as NonEmptyArray[A]) []A { + return as[1:] +} + +func Head[A any](as NonEmptyArray[A]) A { + return as[0] +} + +func First[A any](as NonEmptyArray[A]) A { + return as[0] +} + +func Last[A any](as NonEmptyArray[A]) A { + return as[len(as)-1] +} + +func Size[A any](as NonEmptyArray[A]) int { + return G.Size(as) +} + +func Flatten[A any](mma NonEmptyArray[NonEmptyArray[A]]) NonEmptyArray[A] { + return G.Flatten(mma) +} + +func MonadChain[A, B any](fa NonEmptyArray[A], f func(a A) NonEmptyArray[B]) NonEmptyArray[B] { + return G.MonadChain[NonEmptyArray[A], NonEmptyArray[B]](fa, f) +} + +func Chain[A, B any](f func(A) NonEmptyArray[B]) func(NonEmptyArray[A]) NonEmptyArray[B] { + return G.Chain[NonEmptyArray[A], NonEmptyArray[B]](f) +} + +func MonadAp[B, A any](fab NonEmptyArray[func(A) B], fa NonEmptyArray[A]) NonEmptyArray[B] { + return G.MonadAp[NonEmptyArray[B]](fab, fa) +} + +func Ap[B, A any](fa NonEmptyArray[A]) func(NonEmptyArray[func(A) B]) NonEmptyArray[B] { + return G.Ap[NonEmptyArray[B], NonEmptyArray[func(A) B]](fa) +} + +// FoldMap maps and folds a [NonEmptyArray]. Map the [NonEmptyArray] passing each value to the iterating function. Then fold the results using the provided [Semigroup]. +func FoldMap[A, B any](s S.Semigroup[B]) func(func(A) B) func(NonEmptyArray[A]) B { + return func(f func(A) B) func(NonEmptyArray[A]) B { + return func(as NonEmptyArray[A]) B { + return array.Reduce(Tail(as), func(cur B, a A) B { + return s.Concat(cur, f(a)) + }, f(Head(as))) + } + } +} + +// Fold folds the [NonEmptyArray] using the provided [Semigroup]. +func Fold[A any](s S.Semigroup[A]) func(NonEmptyArray[A]) A { + return func(as NonEmptyArray[A]) A { + return array.Reduce(Tail(as), s.Concat, Head(as)) + } +} + +// Prepend prepends a single value to an array +func Prepend[A any](head A) EM.Endomorphism[NonEmptyArray[A]] { + return array.Prepend[EM.Endomorphism[NonEmptyArray[A]]](head) +} diff --git a/v2/array/sequence.go b/v2/array/sequence.go new file mode 100644 index 0000000..b1b7449 --- /dev/null +++ b/v2/array/sequence.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +// We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces + +// HKTA = HKT +// HKTRA = HKT<[]A> +// HKTFRA = HKT + +// Sequence takes an `Array` where elements are `HKT` (higher kinded type) and, +// using an applicative of that `HKT`, returns an `HKT` of `[]A`. +// e.g. it can turn an `[]Either[error, string]` into an `Either[error, []string]`. +// +// Sequence requires an `Applicative` of the `HKT` you are targeting, e.g. to turn an +// `[]Either[E, A]` into an `Either[E, []A]`, it needs an +// Applicative` for `Either`, to to turn an `[]Option[A]` into an `Option[ []A]`, +// it needs an `Applicative` for `Option`. +func Sequence[A, HKTA, HKTRA, HKTFRA any]( + _of func([]A) HKTRA, + _map func(HKTRA, func([]A) func(A) []A) HKTFRA, + _ap func(HKTFRA, HKTA) HKTRA, +) func([]HKTA) HKTRA { + ca := F.Curry2(Append[A]) + return Reduce(func(fas HKTRA, fa HKTA) HKTRA { + return _ap( + _map(fas, ca), + fa, + ) + }, _of(Empty[A]())) +} + +// ArrayOption returns a function to convert sequence of options into an option of a sequence +func ArrayOption[A any]() func([]O.Option[A]) O.Option[[]A] { + return Sequence( + O.Of[[]A], + O.MonadMap[[]A, func(A) []A], + O.MonadAp[[]A, A], + ) +} diff --git a/v2/array/sequence_test.go b/v2/array/sequence_test.go new file mode 100644 index 0000000..2a41f08 --- /dev/null +++ b/v2/array/sequence_test.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + O "github.com/IBM/fp-go/v2/option" +) + +func TestSequenceOption(t *testing.T) { + seq := ArrayOption[int]() + + assert.Equal(t, O.Of([]int{1, 3}), seq([]O.Option[int]{O.Of(1), O.Of(3)})) + assert.Equal(t, O.None[[]int](), seq([]O.Option[int]{O.Of(1), O.None[int]()})) +} diff --git a/v2/array/sort.go b/v2/array/sort.go new file mode 100644 index 0000000..a3aa1b2 --- /dev/null +++ b/v2/array/sort.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + G "github.com/IBM/fp-go/v2/array/generic" + O "github.com/IBM/fp-go/v2/ord" +) + +// Sort implements a stable sort on the array given the provided ordering +func Sort[T any](ord O.Ord[T]) func(ma []T) []T { + return G.Sort[[]T](ord) +} + +// SortByKey implements a stable sort on the array given the provided ordering on an extracted key +func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T { + return G.SortByKey[[]T](ord, f) +} + +// SortBy implements a stable sort on the array given the provided ordering +func SortBy[T any](ord []O.Ord[T]) func(ma []T) []T { + return G.SortBy[[]T, []O.Ord[T]](ord) +} diff --git a/v2/array/sort_test.go b/v2/array/sort_test.go new file mode 100644 index 0000000..ca85742 --- /dev/null +++ b/v2/array/sort_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "testing" + + O "github.com/IBM/fp-go/v2/ord" + "github.com/stretchr/testify/assert" +) + +func TestSort(t *testing.T) { + + ordInt := O.FromStrictCompare[int]() + + input := []int{2, 1, 3} + + res := Sort(ordInt)(input) + + assert.Equal(t, []int{1, 2, 3}, res) + assert.Equal(t, []int{2, 1, 3}, input) + +} diff --git a/v2/array/testing/laws.go b/v2/array/testing/laws.go new file mode 100644 index 0000000..1a8f65c --- /dev/null +++ b/v2/array/testing/laws.go @@ -0,0 +1,74 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + RA "github.com/IBM/fp-go/v2/array" + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" +) + +// AssertLaws asserts the apply monad laws for the array monad +func AssertLaws[A, B, C any](t *testing.T, + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + return L.AssertLaws(t, + RA.Eq(eqa), + RA.Eq(eqb), + RA.Eq(eqc), + + RA.Of[A], + RA.Of[B], + RA.Of[C], + + RA.Of[func(A) A], + RA.Of[func(A) B], + RA.Of[func(B) C], + RA.Of[func(func(A) B) B], + + RA.MonadMap[A, A], + RA.MonadMap[A, B], + RA.MonadMap[A, C], + RA.MonadMap[B, C], + + RA.MonadMap[func(B) C, func(func(A) B) func(A) C], + + RA.MonadChain[A, A], + RA.MonadChain[A, B], + RA.MonadChain[A, C], + RA.MonadChain[B, C], + + RA.MonadAp[A, A], + RA.MonadAp[B, A], + RA.MonadAp[C, B], + RA.MonadAp[C, A], + + RA.MonadAp[B, func(A) B], + RA.MonadAp[func(A) C, func(A) B], + + ab, + bc, + ) + +} diff --git a/v2/array/testing/laws_test.go b/v2/array/testing/laws_test.go new file mode 100644 index 0000000..43c0f37 --- /dev/null +++ b/v2/array/testing/laws_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqa, eqb, eqc, ab, bc) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/array/traverse.go b/v2/array/traverse.go new file mode 100644 index 0000000..5f3dfaa --- /dev/null +++ b/v2/array/traverse.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import "github.com/IBM/fp-go/v2/internal/array" + +func Traverse[A, B, HKTB, HKTAB, HKTRB any]( + fof func([]B) HKTRB, + fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + f func(A) HKTB) func([]A) HKTRB { + return array.Traverse[[]A](fof, fmap, fap, f) +} + +func MonadTraverse[A, B, HKTB, HKTAB, HKTRB any]( + fof func([]B) HKTRB, + fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + ta []A, + f func(A) HKTB) HKTRB { + + return array.MonadTraverse(fof, fmap, fap, ta, f) +} diff --git a/v2/array/traverse_test.go b/v2/array/traverse_test.go new file mode 100644 index 0000000..8b78b6e --- /dev/null +++ b/v2/array/traverse_test.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "testing" + + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +type ArrayType = []int + +func TestTraverse(t *testing.T) { + + traverse := Traverse( + O.Of[ArrayType], + O.Map[ArrayType, func(int) ArrayType], + O.Ap[ArrayType, int], + + func(n int) O.Option[int] { + if n%2 == 0 { + return O.None[int]() + } + return O.Of(n) + }) + + assert.Equal(t, O.None[[]int](), traverse(ArrayType{1, 2})) + assert.Equal(t, O.Of(ArrayType{1, 3}), traverse(ArrayType{1, 3})) +} diff --git a/v2/array/uniq.go b/v2/array/uniq.go new file mode 100644 index 0000000..4ca5fb8 --- /dev/null +++ b/v2/array/uniq.go @@ -0,0 +1,17 @@ +package array + +import ( + G "github.com/IBM/fp-go/v2/array/generic" +) + +// StrictUniq converts an array of arbitrary items into an array or unique items +// where uniqueness is determined by the built-in uniqueness constraint +func StrictUniq[A comparable](as []A) []A { + return G.StrictUniq[[]A](as) +} + +// Uniq converts an array of arbitrary items into an array or unique items +// where uniqueness is determined based on a key extractor function +func Uniq[A any, K comparable](f func(A) K) func(as []A) []A { + return G.Uniq[[]A](f) +} diff --git a/v2/array/uniq_test.go b/v2/array/uniq_test.go new file mode 100644 index 0000000..0b32749 --- /dev/null +++ b/v2/array/uniq_test.go @@ -0,0 +1,14 @@ +package array + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestUniq(t *testing.T) { + data := From(1, 2, 3, 2, 4, 1) + + uniq := StrictUniq(data) + assert.Equal(t, From(1, 2, 3, 4), uniq) +} diff --git a/v2/array/zip.go b/v2/array/zip.go new file mode 100644 index 0000000..1f9cc28 --- /dev/null +++ b/v2/array/zip.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + G "github.com/IBM/fp-go/v2/array/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// ZipWith applies a function to pairs of elements at the same index in two arrays, collecting the results in a new array. If one +// input array is short, excess elements of the longer array are discarded. +func ZipWith[FCT ~func(A, B) C, A, B, C any](fa []A, fb []B, f FCT) []C { + return G.ZipWith[[]A, []B, []C, FCT](fa, fb, f) +} + +// Zip takes two arrays and returns an array of corresponding pairs. If one input array is short, excess elements of the +// longer array are discarded +func Zip[A, B any](fb []B) func([]A) []T.Tuple2[A, B] { + return G.Zip[[]A, []B, []T.Tuple2[A, B]](fb) +} + +// Unzip is the function is reverse of [Zip]. Takes an array of pairs and return two corresponding arrays +func Unzip[A, B any](cs []T.Tuple2[A, B]) T.Tuple2[[]A, []B] { + return G.Unzip[[]A, []B, []T.Tuple2[A, B]](cs) +} diff --git a/v2/array/zip_test.go b/v2/array/zip_test.go new file mode 100644 index 0000000..0e0e68f --- /dev/null +++ b/v2/array/zip_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "fmt" + "testing" + + T "github.com/IBM/fp-go/v2/tuple" + "github.com/stretchr/testify/assert" +) + +func TestZipWith(t *testing.T) { + left := From(1, 2, 3) + right := From("a", "b", "c", "d") + + res := ZipWith(left, right, func(l int, r string) string { + return fmt.Sprintf("%s%d", r, l) + }) + + assert.Equal(t, From("a1", "b2", "c3"), res) +} + +func TestZip(t *testing.T) { + left := From(1, 2, 3) + right := From("a", "b", "c", "d") + + res := Zip[string](left)(right) + + assert.Equal(t, From(T.MakeTuple2("a", 1), T.MakeTuple2("b", 2), T.MakeTuple2("c", 3)), res) +} + +func TestUnzip(t *testing.T) { + left := From(1, 2, 3) + right := From("a", "b", "c") + + zipped := Zip[string](left)(right) + + unzipped := Unzip(zipped) + + assert.Equal(t, right, unzipped.F1) + assert.Equal(t, left, unzipped.F2) +} diff --git a/v2/assert/assert_test.go b/v2/assert/assert_test.go new file mode 100644 index 0000000..15ed05d --- /dev/null +++ b/v2/assert/assert_test.go @@ -0,0 +1,109 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package assert + +import ( + "fmt" + "testing" + + E "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +var ( + errTest = fmt.Errorf("test failure") + + // Eq is the equal predicate checking if objects are equal + Eq = EQ.FromEquals(assert.ObjectsAreEqual) +) + +func wrap1[T any](wrapped func(t assert.TestingT, expected, actual any, msgAndArgs ...any) bool, t *testing.T, expected T) func(actual T) E.Either[error, T] { + return func(actual T) E.Either[error, T] { + ok := wrapped(t, expected, actual) + if ok { + return E.Of[error](actual) + } + return E.Left[T](errTest) + } +} + +// NotEqual tests if the expected and the actual values are not equal +func NotEqual[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] { + return wrap1(assert.NotEqual, t, expected) +} + +// Equal tests if the expected and the actual values are equal +func Equal[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] { + return wrap1(assert.Equal, t, expected) +} + +// Length tests if an array has the expected length +func Length[T any](t *testing.T, expected int) func(actual []T) E.Either[error, []T] { + return func(actual []T) E.Either[error, []T] { + ok := assert.Len(t, actual, expected) + if ok { + return E.Of[error](actual) + } + return E.Left[[]T](errTest) + } +} + +// NoError validates that there is no error +func NoError[T any](t *testing.T) func(actual E.Either[error, T]) E.Either[error, T] { + return func(actual E.Either[error, T]) E.Either[error, T] { + return E.MonadFold(actual, func(e error) E.Either[error, T] { + assert.NoError(t, e) + return E.Left[T](e) + }, func(value T) E.Either[error, T] { + assert.NoError(t, nil) + return E.Right[error](value) + }) + } +} + +// ArrayContains tests if a value is contained in an array +func ArrayContains[T any](t *testing.T, expected T) func(actual []T) E.Either[error, []T] { + return func(actual []T) E.Either[error, []T] { + ok := assert.Contains(t, actual, expected) + if ok { + return E.Of[error](actual) + } + return E.Left[[]T](errTest) + } +} + +// ContainsKey tests if a key is contained in a map +func ContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] { + return func(actual map[K]T) E.Either[error, map[K]T] { + ok := assert.Contains(t, actual, expected) + if ok { + return E.Of[error](actual) + } + return E.Left[map[K]T](errTest) + } +} + +// NotContainsKey tests if a key is not contained in a map +func NotContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] { + return func(actual map[K]T) E.Either[error, map[K]T] { + ok := assert.NotContains(t, actual, expected) + if ok { + return E.Of[error](actual) + } + return E.Left[map[K]T](errTest) + } +} diff --git a/v2/boolean/boolean.go b/v2/boolean/boolean.go new file mode 100644 index 0000000..5c94d9d --- /dev/null +++ b/v2/boolean/boolean.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package boolean + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + M "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/ord" +) + +var ( + // MonoidAny is the boolean [M.Monoid] under disjunction + MonoidAny = M.MakeMonoid( + func(l, r bool) bool { + return l || r + }, + false, + ) + + // MonoidAll is the boolean [M.Monoid] under conjuction + MonoidAll = M.MakeMonoid( + func(l, r bool) bool { + return l && r + }, + true, + ) + + // Eq is the equals predicate for boolean + Eq = EQ.FromStrictEquals[bool]() + + // Ord is the strict ordering for boolean + Ord = O.MakeOrd(func(l, r bool) int { + if l { + if r { + return 0 + } + return +1 + } + if r { + return -1 + } + return 0 + }, func(l, r bool) bool { + return l == r + }) +) diff --git a/v2/bounded/bounded.go b/v2/bounded/bounded.go new file mode 100644 index 0000000..3e22e41 --- /dev/null +++ b/v2/bounded/bounded.go @@ -0,0 +1,64 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bounded + +import ( + O "github.com/IBM/fp-go/v2/ord" +) + +type Bounded[T any] interface { + O.Ord[T] + Top() T + Bottom() T +} + +type bounded[T any] struct { + c func(x, y T) int + e func(x, y T) bool + t T + b T +} + +func (b bounded[T]) Equals(x, y T) bool { + return b.e(x, y) +} + +func (b bounded[T]) Compare(x, y T) int { + return b.c(x, y) +} + +func (b bounded[T]) Top() T { + return b.t +} + +func (b bounded[T]) Bottom() T { + return b.b +} + +// MakeBounded creates an instance of a bounded type +func MakeBounded[T any](o O.Ord[T], t, b T) Bounded[T] { + return bounded[T]{c: o.Compare, e: o.Equals, t: t, b: b} +} + +// Clamp returns a function that clamps against the bounds defined in the bounded type +func Clamp[T any](b Bounded[T]) func(T) T { + return O.Clamp[T](b)(b.Bottom(), b.Top()) +} + +// Reverse reverses the ordering and swaps the bounds +func Reverse[T any](b Bounded[T]) Bounded[T] { + return MakeBounded(O.Reverse[T](b), b.Bottom(), b.Top()) +} diff --git a/v2/bytes/bytes.go b/v2/bytes/bytes.go new file mode 100644 index 0000000..16086b0 --- /dev/null +++ b/v2/bytes/bytes.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bytes + +func Empty() []byte { + return Monoid.Empty() +} + +func ToString(a []byte) string { + return string(a) +} + +func Size(as []byte) int { + return len(as) +} diff --git a/v2/bytes/monoid.go b/v2/bytes/monoid.go new file mode 100644 index 0000000..77c632f --- /dev/null +++ b/v2/bytes/monoid.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bytes + +import ( + "bytes" + + A "github.com/IBM/fp-go/v2/array" + O "github.com/IBM/fp-go/v2/ord" +) + +var ( + // monoid for byte arrays + Monoid = A.Monoid[byte]() + + // ConcatAll concatenates all bytes + ConcatAll = A.ArrayConcatAll[byte] + + // Ord implements the default ordering on bytes + Ord = O.MakeOrd(bytes.Compare, bytes.Equal) +) diff --git a/v2/bytes/monoid_test.go b/v2/bytes/monoid_test.go new file mode 100644 index 0000000..f5e6a10 --- /dev/null +++ b/v2/bytes/monoid_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bytes + +import ( + "testing" + + M "github.com/IBM/fp-go/v2/monoid/testing" +) + +func TestMonoid(t *testing.T) { + M.AssertLaws(t, Monoid)([][]byte{[]byte(""), []byte("a"), []byte("some value")}) +} diff --git a/v2/cli/apply.go b/v2/cli/apply.go new file mode 100644 index 0000000..9650121 --- /dev/null +++ b/v2/cli/apply.go @@ -0,0 +1,432 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func generateTraverseTuple(f *os.File, i int) { + fmt.Fprintf(f, "\n// TraverseTuple%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i) + fmt.Fprintf(f, "// The function takes a [Tuple%d] of base types and %d functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i, i) + fmt.Fprintf(f, "func TraverseTuple%d[\n", i) + // map as the starting point + fmt.Fprintf(f, " MAP ~func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "func(T%d)", j+1) + } + fmt.Fprintf(f, " ") + fmt.Fprintf(f, "T.") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") func(HKT_T1)") + if i > 1 { + fmt.Fprintf(f, " HKT_F") + for k := 1; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + } else { + fmt.Fprintf(f, " HKT_TUPLE%d", i) + } + fmt.Fprintf(f, ",\n") + // the applicatives + for j := 1; j < i; j++ { + fmt.Fprintf(f, " AP%d ~func(", j) + fmt.Fprintf(f, "HKT_T%d) func(", j+1) + fmt.Fprintf(f, "HKT_F") + for k := j; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + fmt.Fprintf(f, ")") + if j+1 < i { + fmt.Fprintf(f, " HKT_F") + for k := j + 1; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + } else { + fmt.Fprintf(f, " HKT_TUPLE%d", i) + } + fmt.Fprintf(f, ",\n") + } + for j := 0; j < i; j++ { + fmt.Fprintf(f, " F%d ~func(A%d) HKT_T%d,\n", j+1, j+1, j+1) + } + for j := 0; j < i; j++ { + fmt.Fprintf(f, " A%d, T%d,\n", j+1, j+1) + } + for j := 0; j < i; j++ { + fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1) + } + for j := 1; j < i; j++ { + fmt.Fprintf(f, " HKT_F") + for k := j; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + fmt.Fprintf(f, ", // HKT[") + for k := j; k < i; k++ { + fmt.Fprintf(f, "func(T%d) ", k+1) + } + fmt.Fprintf(f, "T.") + writeTupleType(f, "T", i) + fmt.Fprintf(f, "]\n") + } + fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i) + writeTupleType(f, "T", i) + fmt.Fprintf(f, "]\n") + fmt.Fprintf(f, "](\n") + + // the callbacks + fmt.Fprintf(f, " fmap MAP,\n") + for j := 1; j < i; j++ { + fmt.Fprintf(f, " fap%d AP%d,\n", j, j) + } + // the transformer functions + for j := 1; j <= i; j++ { + fmt.Fprintf(f, " f%d F%d,\n", j, j) + } + // the parameters + fmt.Fprintf(f, " t T.Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "A%d", j+1) + } + fmt.Fprintf(f, "],\n") + fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i) + + fmt.Fprintf(f, " return F.Pipe%d(\n", i) + fmt.Fprintf(f, " f1(t.F1),\n") + fmt.Fprintf(f, " fmap(tupleConstructor%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "]()),\n") + for j := 1; j < i; j++ { + fmt.Fprintf(f, " fap%d(f%d(t.F%d)),\n", j, j+1, j+1) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") +} + +func generateSequenceTuple(f *os.File, i int) { + fmt.Fprintf(f, "\n// SequenceTuple%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i) + fmt.Fprintf(f, "// The function takes a [Tuple%d] of higher higher kinded types and returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i) + fmt.Fprintf(f, "func SequenceTuple%d[\n", i) + // map as the starting point + fmt.Fprintf(f, " MAP ~func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "func(T%d)", j+1) + } + fmt.Fprintf(f, " ") + fmt.Fprintf(f, "T.") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") func(HKT_T1)") + if i > 1 { + fmt.Fprintf(f, " HKT_F") + for k := 1; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + } else { + fmt.Fprintf(f, " HKT_TUPLE%d", i) + } + fmt.Fprintf(f, ",\n") + // the applicatives + for j := 1; j < i; j++ { + fmt.Fprintf(f, " AP%d ~func(", j) + fmt.Fprintf(f, "HKT_T%d) func(", j+1) + fmt.Fprintf(f, "HKT_F") + for k := j; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + fmt.Fprintf(f, ")") + if j+1 < i { + fmt.Fprintf(f, " HKT_F") + for k := j + 1; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + } else { + fmt.Fprintf(f, " HKT_TUPLE%d", i) + } + fmt.Fprintf(f, ",\n") + } + + for j := 0; j < i; j++ { + fmt.Fprintf(f, " T%d,\n", j+1) + } + for j := 0; j < i; j++ { + fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1) + } + for j := 1; j < i; j++ { + fmt.Fprintf(f, " HKT_F") + for k := j; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + fmt.Fprintf(f, ", // HKT[") + for k := j; k < i; k++ { + fmt.Fprintf(f, "func(T%d) ", k+1) + } + fmt.Fprintf(f, "T.") + writeTupleType(f, "T", i) + fmt.Fprintf(f, "]\n") + } + fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i) + writeTupleType(f, "T", i) + fmt.Fprintf(f, "]\n") + fmt.Fprintf(f, "](\n") + + // the callbacks + fmt.Fprintf(f, " fmap MAP,\n") + for j := 1; j < i; j++ { + fmt.Fprintf(f, " fap%d AP%d,\n", j, j) + } + // the parameters + fmt.Fprintf(f, " t T.Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "HKT_T%d", j+1) + } + fmt.Fprintf(f, "],\n") + fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i) + + fmt.Fprintf(f, " return F.Pipe%d(\n", i) + fmt.Fprintf(f, " t.F1,\n") + fmt.Fprintf(f, " fmap(tupleConstructor%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "]()),\n") + for j := 1; j < i; j++ { + fmt.Fprintf(f, " fap%d(t.F%d),\n", j, j+1) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") +} + +func generateSequenceT(f *os.File, i int) { + fmt.Fprintf(f, "\n// SequenceT%d is a utility function used to implement the sequence operation for higher kinded types based only on map and ap.\n", i) + fmt.Fprintf(f, "// The function takes %d higher higher kinded types and returns a higher kinded type of a [Tuple%d] with the resolved values.\n", i, i) + fmt.Fprintf(f, "func SequenceT%d[\n", i) + // map as the starting point + fmt.Fprintf(f, " MAP ~func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "func(T%d)", j+1) + } + fmt.Fprintf(f, " ") + fmt.Fprintf(f, "T.") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") func(HKT_T1)") + if i > 1 { + fmt.Fprintf(f, " HKT_F") + for k := 1; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + } else { + fmt.Fprintf(f, " HKT_TUPLE%d", i) + } + fmt.Fprintf(f, ",\n") + // the applicatives + for j := 1; j < i; j++ { + fmt.Fprintf(f, " AP%d ~func(", j) + fmt.Fprintf(f, "HKT_T%d) func(", j+1) + fmt.Fprintf(f, "HKT_F") + for k := j; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + fmt.Fprintf(f, ")") + if j+1 < i { + fmt.Fprintf(f, " HKT_F") + for k := j + 1; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + } else { + fmt.Fprintf(f, " HKT_TUPLE%d", i) + } + fmt.Fprintf(f, ",\n") + } + + for j := 0; j < i; j++ { + fmt.Fprintf(f, " T%d,\n", j+1) + } + for j := 0; j < i; j++ { + fmt.Fprintf(f, " HKT_T%d, // HKT[T%d]\n", j+1, j+1) + } + for j := 1; j < i; j++ { + fmt.Fprintf(f, " HKT_F") + for k := j; k < i; k++ { + fmt.Fprintf(f, "_T%d", k+1) + } + fmt.Fprintf(f, ", // HKT[") + for k := j; k < i; k++ { + fmt.Fprintf(f, "func(T%d) ", k+1) + } + fmt.Fprintf(f, "T.") + writeTupleType(f, "T", i) + fmt.Fprintf(f, "]\n") + } + fmt.Fprintf(f, " HKT_TUPLE%d any, // HKT[", i) + writeTupleType(f, "T", i) + fmt.Fprintf(f, "]\n") + fmt.Fprintf(f, "](\n") + + // the callbacks + fmt.Fprintf(f, " fmap MAP,\n") + for j := 1; j < i; j++ { + fmt.Fprintf(f, " fap%d AP%d,\n", j, j) + } + // the parameters + for j := 0; j < i; j++ { + fmt.Fprintf(f, " t%d HKT_T%d,\n", j+1, j+1) + } + fmt.Fprintf(f, ") HKT_TUPLE%d {\n", i) + + fmt.Fprintf(f, " return F.Pipe%d(\n", i) + fmt.Fprintf(f, " t1,\n") + fmt.Fprintf(f, " fmap(tupleConstructor%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "]()),\n") + for j := 1; j < i; j++ { + fmt.Fprintf(f, " fap%d(t%d),\n", j, j+1) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") +} + +func generateTupleConstructor(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// tupleConstructor%d returns a curried version of [T.MakeTuple%d]\n", i, i) + fmt.Fprintf(f, "func tupleConstructor%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any]()") + for j := 0; j < i; j++ { + fmt.Fprintf(f, " func(T%d)", j+1) + } + fmt.Fprintf(f, " T.Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "] {\n") + + fmt.Fprintf(f, " return F.Curry%d(T.MakeTuple%d[", i, i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "])\n") + + fmt.Fprintf(f, "}\n") +} + +func generateApplyHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + // print out some helpers + fmt.Fprintf(f, ` +import ( + F "github.com/IBM/fp-go/v2/function" + T "github.com/IBM/fp-go/v2/tuple" +) +`) + + for i := 1; i <= count; i++ { + // tuple constructor + generateTupleConstructor(f, i) + // sequenceT + generateSequenceT(f, i) + // sequenceTuple + generateSequenceTuple(f, i) + // traverseTuple + generateTraverseTuple(f, i) + } + + return nil +} + +func ApplyCommand() *C.Command { + return &C.Command{ + Name: "apply", + Usage: "generate code for the sequence operations of apply", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateApplyHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/bind.go b/v2/cli/bind.go new file mode 100644 index 0000000..1f373b8 --- /dev/null +++ b/v2/cli/bind.go @@ -0,0 +1,294 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func createCombinations(n int, all, prev []int) [][]int { + l := len(prev) + if l == n { + return [][]int{prev} + } + var res [][]int + for idx, val := range all { + cpy := make([]int, l+1) + copy(cpy, prev) + cpy[l] = val + + res = append(res, createCombinations(n, all[idx+1:], cpy)...) + } + return res +} + +func remaining(comb []int, total int) []int { + var res []int + mp := make(map[int]int) + for _, idx := range comb { + mp[idx] = idx + } + for i := 1; i <= total; i++ { + _, ok := mp[i] + if !ok { + res = append(res, i) + } + } + return res +} + +func generateCombSingleBind(f *os.File, comb [][]int, total int) { + for _, c := range comb { + // remaining indexes + rem := remaining(c, total) + + // bind function + fmt.Fprintf(f, "\n// Bind") + for _, idx := range c { + fmt.Fprintf(f, "%d", idx) + } + fmt.Fprintf(f, "of%d takes a function with %d parameters and returns a new function with %d parameters that will bind these parameters to the positions [", total, total, len(c)) + for i, idx := range c { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "%d", idx) + } + fmt.Fprintf(f, "] of the original function.\n// The return value of is a function with the remaining %d parameters at positions [", len(rem)) + for i, idx := range rem { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "%d", idx) + } + fmt.Fprintf(f, "] of the original function.\n") + fmt.Fprintf(f, "func Bind") + for _, idx := range c { + fmt.Fprintf(f, "%d", idx) + } + fmt.Fprintf(f, "of%d[F ~func(", total) + for i := 0; i < total; i++ { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", i+1) + } + fmt.Fprintf(f, ") R") + for i := 0; i < total; i++ { + fmt.Fprintf(f, ", T%d", i+1) + } + fmt.Fprintf(f, ", R any](f F) func(") + for i, idx := range c { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", idx) + } + fmt.Fprintf(f, ") func(") + for i, idx := range rem { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", idx) + } + fmt.Fprintf(f, ") R {\n") + + fmt.Fprintf(f, " return func(") + + for i, idx := range c { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", idx, idx) + } + fmt.Fprintf(f, ") func(") + for i, idx := range rem { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", idx) + } + fmt.Fprintf(f, ") R {\n") + + fmt.Fprintf(f, " return func(") + for i, idx := range rem { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", idx, idx) + } + fmt.Fprintf(f, ") R {\n") + + fmt.Fprintf(f, " return f(") + for i := 1; i <= total; i++ { + if i > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", i) + } + fmt.Fprintf(f, ")\n") + + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, "}\n") + + // ignore function + fmt.Fprintf(f, "\n// Ignore") + for _, idx := range c { + fmt.Fprintf(f, "%d", idx) + } + fmt.Fprintf(f, "of%d takes a function with %d parameters and returns a new function with %d parameters that will ignore the values at positions [", total, len(rem), total) + for i, idx := range c { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "%d", idx) + } + fmt.Fprintf(f, "] and pass the remaining %d parameters to the original function\n", len(rem)) + fmt.Fprintf(f, "func Ignore") + for _, idx := range c { + fmt.Fprintf(f, "%d", idx) + } + fmt.Fprintf(f, "of%d[", total) + // start with the undefined parameters + for i, idx := range c { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", idx) + } + if len(c) > 0 { + fmt.Fprintf(f, " any, ") + } + fmt.Fprintf(f, "F ~func(") + for i, idx := range rem { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", idx) + } + fmt.Fprintf(f, ") R") + for _, idx := range rem { + fmt.Fprintf(f, ", T%d", idx) + } + fmt.Fprintf(f, ", R any](f F) func(") + for i := 0; i < total; i++ { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", i+1) + } + fmt.Fprintf(f, ") R {\n") + + fmt.Fprintf(f, " return func(") + for i := 0; i < total; i++ { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", i+1, i+1) + } + fmt.Fprintf(f, ") R {\n") + fmt.Fprintf(f, " return f(") + for i, idx := range rem { + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", idx) + } + fmt.Fprintf(f, ")\n") + + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, "}\n") + + } +} + +func generateSingleBind(f *os.File, total int) { + + fmt.Fprintf(f, "// Combinations for a total of %d arguments\n", total) + + // construct the indexes + all := make([]int, total) + for i := 0; i < total; i++ { + all[i] = i + 1 + } + // for all permutations of a certain length + for j := 0; j < total; j++ { + // get combinations of that size + comb := createCombinations(j+1, all, []int{}) + generateCombSingleBind(f, comb, total) + } +} + +func generateBind(f *os.File, i int) { + for j := 1; j < i; j++ { + generateSingleBind(f, j) + } +} + +func generateBindHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n", pkg) + + generateBind(f, count) + + return nil +} + +func BindCommand() *C.Command { + return &C.Command{ + Name: "bind", + Usage: "generate code for binder functions etc", + Description: "Code generation for bind, etc", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateBindHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/commands.go b/v2/cli/commands.go new file mode 100644 index 0000000..1a363d5 --- /dev/null +++ b/v2/cli/commands.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + C "github.com/urfave/cli/v2" +) + +func Commands() []*C.Command { + return []*C.Command{ + PipeCommand(), + IdentityCommand(), + OptionCommand(), + EitherCommand(), + TupleCommand(), + BindCommand(), + ApplyCommand(), + ContextReaderIOEitherCommand(), + ReaderIOEitherCommand(), + ReaderCommand(), + IOEitherCommand(), + IOCommand(), + IOOptionCommand(), + DICommand(), + } +} diff --git a/v2/cli/common.go b/v2/cli/common.go new file mode 100644 index 0000000..d6dcdd5 --- /dev/null +++ b/v2/cli/common.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + C "github.com/urfave/cli/v2" +) + +const ( + keyFilename = "filename" + keyCount = "count" +) + +var ( + flagFilename = &C.StringFlag{ + Name: keyFilename, + Value: "gen.go", + Usage: "Name of the generated file", + } + + flagCount = &C.IntFlag{ + Name: keyCount, + Value: 20, + Usage: "Number of variations to create", + } +) diff --git a/v2/cli/contextreaderioeither.go b/v2/cli/contextreaderioeither.go new file mode 100644 index 0000000..b40c378 --- /dev/null +++ b/v2/cli/contextreaderioeither.go @@ -0,0 +1,525 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + + C "github.com/urfave/cli/v2" +) + +func generateNestedCallbacks(i, total int) string { + var buf strings.Builder + for j := i; j < total; j++ { + if j > i { + buf.WriteString(" ") + } + buf.WriteString(fmt.Sprintf("func(T%d)", j+1)) + } + if i > 0 { + buf.WriteString(" ") + } + buf.WriteString(tupleType("T")(total)) + return buf.String() +} + +func generateContextReaderIOEitherTraverseTuple(suffix string) func(f, fg *os.File, i int) { + return func(f, fg *os.File, i int) { + // tupleT type + tupleT := tupleType("T")(i) + tupleA := tupleType("A")(i) + + // non-generic version + // generic version + fmt.Fprintf(f, "\n// Traverse%sTuple%d converts a [T.Tuple%d] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple%d].\n", suffix, i, i, i) + fmt.Fprintf(f, "func Traverse%sTuple%d[", suffix, i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "F%d ~func(A%d) ReaderIOEither[T%d]", j+1, j+1, j+1) + } + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", A%d, T%d", j+1, j+1) + } + fmt.Fprintf(f, " any](") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d F%d", j+1, j+1) + } + fmt.Fprintf(f, ") func(%s) ReaderIOEither[%s] {\n", tupleA, tupleT) + fmt.Fprintf(f, " return G.Traverse%sTuple%d[ReaderIOEither[%s]](", suffix, i, tupleT) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d", j+1) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") + + // generic version + fmt.Fprintf(fg, "\n// Traverse%sTuple%d converts a [T.Tuple%d] of readers into a reader of a [T.Tuple%d].\n", suffix, i, i, i) + fmt.Fprintf(fg, "func Traverse%sTuple%d[\n", suffix, i) + fmt.Fprintf(fg, " GR_TUPLE%d ~func(context.Context) GIO_TUPLE%d,\n", i, i) + // the transformation functions + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " F%d ~func(A%d) GR_T%d,\n", j+1, j+1, j+1) + } + // the readers + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " GR_T%d ~func(context.Context) GIO_T%d,\n", j+1, j+1) + } + // the tuples + fmt.Fprintf(fg, " GIO_TUPLE%d ~func() E.Either[error, %s],\n", i, tupleT) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " GIO_T%d ~func() E.Either[error, T%d],\n", j+1, j+1) + } + // input and result parameters + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " A%d,\n", j+1) + fmt.Fprintf(fg, " T%d", j+1) + if j < i-1 { + fmt.Fprintf(fg, ",\n") + } + } + fmt.Fprintf(fg, " any](") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "f%d F%d", j+1, j+1) + } + fmt.Fprintf(fg, ") func(%s) GR_TUPLE%d {\n", tupleA, i) + fmt.Fprintf(fg, " return func(t %s) GR_TUPLE%d {\n", tupleA, i) + fmt.Fprintf(fg, " return A.TraverseTuple%d(\n", i) + // map call + var cr string + if i > 1 { + cb := generateNestedCallbacks(1, i) + cio := fmt.Sprintf("func() E.Either[error, %s]", cb) + cr = fmt.Sprintf("func(context.Context) %s", cio) + } else { + cr = fmt.Sprintf("GR_TUPLE%d", i) + } + fmt.Fprintf(fg, " Map[GR_T%d, %s, GIO_T%d],\n", 1, cr, 1) + // the apply calls + for j := 1; j < i; j++ { + if j < i-1 { + cb := generateNestedCallbacks(j+1, i) + cio := fmt.Sprintf("func() E.Either[error, %s]", cb) + cr = fmt.Sprintf("func(context.Context) %s", cio) + } else { + cr = fmt.Sprintf("GR_TUPLE%d", i) + } + fmt.Fprintf(fg, " Ap%s[%s, func(context.Context) func() E.Either[error, %s], GR_T%d],\n", suffix, cr, generateNestedCallbacks(j, i), j+1) + } + // function parameters + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " f%d,\n", j+1) + } + + // raw parameters + fmt.Fprintf(fg, " t,\n") + + fmt.Fprintf(fg, " )\n") + fmt.Fprintf(fg, " }\n") + fmt.Fprintf(fg, "}\n") + } +} + +func generateContextReaderIOEitherSequenceTuple(suffix string) func(f, fg *os.File, i int) { + return func(f, fg *os.File, i int) { + // tuple type + tuple := tupleType("T")(i) + + // non-generic version + // generic version + fmt.Fprintf(f, "\n// Sequence%sTuple%d converts a [T.Tuple%d] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple%d].\n", suffix, i, i, i) + fmt.Fprintf(f, "func Sequence%sTuple%d[", suffix, i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any](t T.Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "ReaderIOEither[T%d]", j+1) + } + fmt.Fprintf(f, "]) ReaderIOEither[%s] {\n", tuple) + fmt.Fprintf(f, " return G.Sequence%sTuple%d[ReaderIOEither[%s]](t)\n", suffix, i, tuple) + fmt.Fprintf(f, "}\n") + + // generic version + fmt.Fprintf(fg, "\n// Sequence%sTuple%d converts a [T.Tuple%d] of readers into a reader of a [T.Tuple%d].\n", suffix, i, i, i) + fmt.Fprintf(fg, "func Sequence%sTuple%d[\n", suffix, i) + + fmt.Fprintf(fg, " GR_TUPLE%d ~func(context.Context) GIO_TUPLE%d,\n", i, i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " GR_T%d ~func(context.Context) GIO_T%d,\n", j+1, j+1) + } + + fmt.Fprintf(fg, " GIO_TUPLE%d ~func() E.Either[error, %s],\n", i, tuple) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " GIO_T%d ~func() E.Either[error, T%d],\n", j+1, j+1) + } + + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " T%d", j+1) + if j < i-1 { + fmt.Fprintf(fg, ",\n") + } + } + fmt.Fprintf(fg, " any](t T.Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "GR_T%d", j+1) + } + fmt.Fprintf(fg, "]) GR_TUPLE%d {\n", i) + fmt.Fprintf(fg, " return A.SequenceTuple%d(\n", i) + // map call + var cr string + if i > 1 { + cb := generateNestedCallbacks(1, i) + cio := fmt.Sprintf("func() E.Either[error, %s]", cb) + cr = fmt.Sprintf("func(context.Context) %s", cio) + } else { + cr = fmt.Sprintf("GR_TUPLE%d", i) + } + fmt.Fprintf(fg, " Map[GR_T%d, %s, GIO_T%d],\n", 1, cr, 1) + // the apply calls + for j := 1; j < i; j++ { + if j < i-1 { + cb := generateNestedCallbacks(j+1, i) + cio := fmt.Sprintf("func() E.Either[error, %s]", cb) + cr = fmt.Sprintf("func(context.Context) %s", cio) + } else { + cr = fmt.Sprintf("GR_TUPLE%d", i) + } + fmt.Fprintf(fg, " Ap%s[%s, func(context.Context) func() E.Either[error, %s], GR_T%d],\n", suffix, cr, generateNestedCallbacks(j, i), j+1) + } + // raw parameters + fmt.Fprintf(fg, " t,\n") + + fmt.Fprintf(fg, " )\n") + fmt.Fprintf(fg, "}\n") + } +} + +func generateContextReaderIOEitherSequenceT(suffix string) func(f, fg *os.File, i int) { + return func(f, fg *os.File, i int) { + // tuple type + tuple := tupleType("T")(i) + + // non-generic version + // generic version + fmt.Fprintf(f, "\n// Sequence%sT%d converts %d [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple%d].\n", suffix, i, i, i) + fmt.Fprintf(f, "func Sequence%sT%d[", suffix, i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any](") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d ReaderIOEither[T%d]", j+1, j+1) + } + fmt.Fprintf(f, ") ReaderIOEither[%s] {\n", tuple) + fmt.Fprintf(f, " return G.Sequence%sT%d[ReaderIOEither[%s]](", suffix, i, tuple) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j+1) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") + + // generic version + fmt.Fprintf(fg, "\n// Sequence%sT%d converts %d readers into a reader of a [T.Tuple%d].\n", suffix, i, i, i) + fmt.Fprintf(fg, "func Sequence%sT%d[\n", suffix, i) + + fmt.Fprintf(fg, " GR_TUPLE%d ~func(context.Context) GIO_TUPLE%d,\n", i, i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " GR_T%d ~func(context.Context) GIO_T%d,\n", j+1, j+1) + } + + fmt.Fprintf(fg, " GIO_TUPLE%d ~func() E.Either[error, %s],\n", i, tuple) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " GIO_T%d ~func() E.Either[error, T%d],\n", j+1, j+1) + } + + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " T%d", j+1) + if j < i-1 { + fmt.Fprintf(fg, ",\n") + } + } + fmt.Fprintf(fg, " any](\n") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " t%d GR_T%d,\n", j+1, j+1) + } + fmt.Fprintf(fg, ") GR_TUPLE%d {\n", i) + fmt.Fprintf(fg, " return A.SequenceT%d(\n", i) + // map call + var cr string + if i > 1 { + cb := generateNestedCallbacks(1, i) + cio := fmt.Sprintf("func() E.Either[error, %s]", cb) + cr = fmt.Sprintf("func(context.Context) %s", cio) + } else { + cr = fmt.Sprintf("GR_TUPLE%d", i) + } + fmt.Fprintf(fg, " Map[GR_T%d, %s, GIO_T%d],\n", 1, cr, 1) + // the apply calls + for j := 1; j < i; j++ { + if j < i-1 { + cb := generateNestedCallbacks(j+1, i) + cio := fmt.Sprintf("func() E.Either[error, %s]", cb) + cr = fmt.Sprintf("func(context.Context) %s", cio) + } else { + cr = fmt.Sprintf("GR_TUPLE%d", i) + } + fmt.Fprintf(fg, " Ap%s[%s, func(context.Context) func() E.Either[error, %s], GR_T%d],\n", suffix, cr, generateNestedCallbacks(j, i), j+1) + } + // raw parameters + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " t%d,\n", j+1) + } + + fmt.Fprintf(fg, " )\n") + fmt.Fprintf(fg, "}\n") + } +} + +func generateContextReaderIOEitherEitherize(f, fg *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[R]]\n// The inverse function is [Uneitherize%d]\n", i, i, i, i) + fmt.Fprintf(f, "func Eitherize%d[F ~func(context.Context", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ") (R, error)") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") ReaderIOEither[R] {\n") + fmt.Fprintf(f, " return G.Eitherize%d[ReaderIOEither[R]](f)\n", i) + fmt.Fprintln(f, "}") + + // generic version + fmt.Fprintf(fg, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The inverse function is [Uneitherize%d]\n", i, i, i, i) + fmt.Fprintf(fg, "func Eitherize%d[GRA ~func(context.Context) GIOA, F ~func(context.Context", i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j) + } + fmt.Fprintf(fg, ") GRA {\n") + fmt.Fprintf(fg, " return RE.Eitherize%d[GRA](f)\n", i) + fmt.Fprintln(fg, "}") +} + +func generateContextReaderIOEitherUneitherize(f, fg *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning a [ReaderIOEither[R]] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the [context.Context].\n", i, i+1, i) + fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") ReaderIOEither[R]") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", R any](f F) func(context.Context") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ") (R, error) {\n") + fmt.Fprintf(f, " return G.Uneitherize%d[ReaderIOEither[R]", i) + + fmt.Fprintf(f, ", func(context.Context") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ")(R, error)](f)\n") + fmt.Fprintln(f, "}") + + // generic version + fmt.Fprintf(fg, "\n// Uneitherize%d converts a function with %d parameters returning a [GRA] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the [context.Context].\n", i, i, i) + fmt.Fprintf(fg, "func Uneitherize%d[GRA ~func(context.Context) GIOA, F ~func(context.Context", i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ", R any](f func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j) + } + fmt.Fprintf(fg, ") GRA) F {\n") + + fmt.Fprintf(fg, " return func(c context.Context") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", t%d T%d", j, j) + } + fmt.Fprintf(fg, ") (R, error) {\n") + fmt.Fprintf(fg, " return E.UnwrapError(f(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "t%d", j) + } + fmt.Fprintf(fg, ")(c)())\n") + fmt.Fprintf(fg, " }\n") + fmt.Fprintf(fg, "}\n") +} + +func generateContextReaderIOEitherHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // construct subdirectory + genFilename := filepath.Join("generic", filename) + err = os.MkdirAll("generic", os.ModePerm) + if err != nil { + return err + } + fg, err := os.Create(filepath.Clean(genFilename)) + if err != nil { + return err + } + defer fg.Close() + + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + writePackage(f, pkg) + + fmt.Fprintf(f, ` +import ( + "context" + + G "github.com/IBM/fp-go/v2/context/%s/generic" + T "github.com/IBM/fp-go/v2/tuple" +) +`, pkg) + + writePackage(fg, "generic") + + fmt.Fprintf(fg, ` +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + RE "github.com/IBM/fp-go/v2/readerioeither/generic" + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) +`) + + generateContextReaderIOEitherEitherize(f, fg, 0) + generateContextReaderIOEitherUneitherize(f, fg, 0) + + for i := 1; i <= count; i++ { + // eitherize + generateContextReaderIOEitherEitherize(f, fg, i) + generateContextReaderIOEitherUneitherize(f, fg, i) + // sequenceT + generateContextReaderIOEitherSequenceT("")(f, fg, i) + generateContextReaderIOEitherSequenceT("Seq")(f, fg, i) + generateContextReaderIOEitherSequenceT("Par")(f, fg, i) + // sequenceTuple + generateContextReaderIOEitherSequenceTuple("")(f, fg, i) + generateContextReaderIOEitherSequenceTuple("Seq")(f, fg, i) + generateContextReaderIOEitherSequenceTuple("Par")(f, fg, i) + // traverseTuple + generateContextReaderIOEitherTraverseTuple("")(f, fg, i) + generateContextReaderIOEitherTraverseTuple("Seq")(f, fg, i) + generateContextReaderIOEitherTraverseTuple("Par")(f, fg, i) + } + + return nil +} + +func ContextReaderIOEitherCommand() *C.Command { + return &C.Command{ + Name: "contextreaderioeither", + Usage: "generate code for ContextReaderIOEither", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateContextReaderIOEitherHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/di.go b/v2/cli/di.go new file mode 100644 index 0000000..dd1e5dd --- /dev/null +++ b/v2/cli/di.go @@ -0,0 +1,231 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func generateMakeProvider(f *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// MakeProvider%d creates a [DIE.Provider] for an [InjectionToken] from a function with %d dependencies\n", i, i) + fmt.Fprintf(f, "func MakeProvider%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any, R any](\n") + fmt.Fprintf(f, " token InjectionToken[R],\n") + for j := 0; j < i; j++ { + fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1) + } + fmt.Fprintf(f, " f func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") IOE.IOEither[error, R],\n") + fmt.Fprintf(f, ") DIE.Provider {\n") + fmt.Fprint(f, " return DIE.MakeProvider(\n") + fmt.Fprint(f, " token,\n") + fmt.Fprintf(f, " MakeProviderFactory%d(\n", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, " d%d,\n", j+1) + } + fmt.Fprint(f, " f,\n") + fmt.Fprint(f, " ))\n") + fmt.Fprintf(f, "}\n") +} + +func generateMakeTokenWithDefault(f *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// MakeTokenWithDefault%d creates an [InjectionToken] with a default implementation with %d dependencies\n", i, i) + fmt.Fprintf(f, "func MakeTokenWithDefault%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any, R any](\n") + fmt.Fprintf(f, " name string,\n") + for j := 0; j < i; j++ { + fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1) + } + fmt.Fprintf(f, " f func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") IOE.IOEither[error, R],\n") + fmt.Fprintf(f, ") InjectionToken[R] {\n") + fmt.Fprintf(f, " return MakeTokenWithDefault[R](name, MakeProviderFactory%d(\n", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, " d%d,\n", j+1) + } + fmt.Fprint(f, " f,\n") + fmt.Fprint(f, " ))\n") + fmt.Fprintf(f, "}\n") +} + +func generateMakeProviderFactory(f *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// MakeProviderFactory%d creates a [DIE.ProviderFactory] from a function with %d arguments and %d dependencies\n", i, i, i) + fmt.Fprintf(f, "func MakeProviderFactory%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any, R any](\n") + for j := 0; j < i; j++ { + fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1) + } + fmt.Fprintf(f, " f func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") IOE.IOEither[error, R],\n") + fmt.Fprintf(f, ") DIE.ProviderFactory {\n") + fmt.Fprint(f, " return DIE.MakeProviderFactory(\n") + fmt.Fprint(f, " A.From[DIE.Dependency](\n") + for j := 0; j < i; j++ { + fmt.Fprintf(f, " d%d,\n", j+1) + } + fmt.Fprint(f, " ),\n") + fmt.Fprintf(f, " eraseProviderFactory%d(\n", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, " d%d,\n", j+1) + } + fmt.Fprint(f, " f,\n") + fmt.Fprint(f, " ),\n") + fmt.Fprint(f, " )\n") + fmt.Fprintf(f, "}\n") +} + +func generateEraseProviderFactory(f *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// eraseProviderFactory%d creates a function that takes a variadic number of untyped arguments and from a function of %d strongly typed arguments and %d dependencies\n", i, i, i) + fmt.Fprintf(f, "func eraseProviderFactory%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any, R any](\n") + for j := 0; j < i; j++ { + fmt.Fprintf(f, " d%d Dependency[T%d],\n", j+1, j+1) + } + fmt.Fprintf(f, " f func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {\n") + fmt.Fprintf(f, " ft := eraseTuple(T.Tupled%d(f))\n", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, " t%d := lookupAt[T%d](%d, d%d)\n", j+1, j+1, j, j+1) + } + fmt.Fprint(f, " return func(params ...any) IOE.IOEither[error, any] {\n") + fmt.Fprintf(f, " return ft(E.SequenceT%d(\n", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, " t%d(params),\n", j+1) + } + fmt.Fprint(f, " ))\n") + fmt.Fprint(f, " }\n") + fmt.Fprintf(f, "}\n") +} + +func generateDIHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprint(f, ` +import ( + E "github.com/IBM/fp-go/v2/either" + IOE "github.com/IBM/fp-go/v2/ioeither" + T "github.com/IBM/fp-go/v2/tuple" + A "github.com/IBM/fp-go/v2/array" + DIE "github.com/IBM/fp-go/v2/di/erasure" +) +`) + + for i := 1; i <= count; i++ { + generateEraseProviderFactory(f, i) + generateMakeProviderFactory(f, i) + generateMakeTokenWithDefault(f, i) + generateMakeProvider(f, i) + } + + return nil +} + +func DICommand() *C.Command { + return &C.Command{ + Name: "di", + Usage: "generate code for the dependency injection package", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateDIHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/either.go b/v2/cli/either.go new file mode 100644 index 0000000..f306d14 --- /dev/null +++ b/v2/cli/either.go @@ -0,0 +1,200 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func eitherHKT(typeE string) func(typeA string) string { + return func(typeA string) string { + return fmt.Sprintf("Either[%s, %s]", typeE, typeA) + } +} + +func generateEitherTraverseTuple(f *os.File, i int) { + generateTraverseTuple1(eitherHKT("E"), "E")(f, i) +} + +func generateEitherSequenceTuple(f *os.File, i int) { + generateSequenceTuple1(eitherHKT("E"), "E")(f, i) +} + +func generateEitherSequenceT(f *os.File, i int) { + generateSequenceT1(eitherHKT("E"), "E")(f, i) +} + +func generateUneitherize(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning an Either into a function with %d parameters returning a tuple\n// The inverse function is [Eitherize%d]\n", i, i, i, i) + fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") Either[error, R]") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") (R, error) {\n") + fmt.Fprintf(f, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j, j) + } + fmt.Fprintf(f, ") (R, error) {\n") + fmt.Fprintf(f, " return UnwrapError(f(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j) + } + fmt.Fprintln(f, "))") + fmt.Fprintln(f, " }") + fmt.Fprintln(f, "}") +} + +func generateEitherize(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning an Either\n// The inverse function is [Uneitherize%d]\n", i, i, i, i) + fmt.Fprintf(f, "func Eitherize%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") (R, error)") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") Either[error, R] {\n") + fmt.Fprintf(f, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j, j) + } + fmt.Fprintf(f, ") Either[error, R] {\n") + fmt.Fprintf(f, " return TryCatchError(f(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j) + } + fmt.Fprintln(f, "))") + fmt.Fprintln(f, " }") + fmt.Fprintln(f, "}") +} + +func generateEitherHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) +`) + + // zero level functions + + // optionize + generateEitherize(f, 0) + // unoptionize + generateUneitherize(f, 0) + + for i := 1; i <= count; i++ { + // optionize + generateEitherize(f, i) + // unoptionize + generateUneitherize(f, i) + // sequenceT + generateEitherSequenceT(f, i) + // sequenceTuple + generateEitherSequenceTuple(f, i) + // traverseTuple + generateEitherTraverseTuple(f, i) + } + + return nil +} + +func EitherCommand() *C.Command { + return &C.Command{ + Name: "either", + Usage: "generate code for Either", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateEitherHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/header.go b/v2/cli/header.go new file mode 100644 index 0000000..591e848 --- /dev/null +++ b/v2/cli/header.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "os" + "time" +) + +func writePackage(f *os.File, pkg string) { + // print package + fmt.Fprintf(f, "package %s\n\n", pkg) + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) +} diff --git a/v2/cli/identity.go b/v2/cli/identity.go new file mode 100644 index 0000000..238690e --- /dev/null +++ b/v2/cli/identity.go @@ -0,0 +1,103 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func identityHKT(typeA string) string { + return typeA +} + +func generateIdentityTraverseTuple(f *os.File, i int) { + generateTraverseTuple1(identityHKT, "")(f, i) +} + +func generateIdentitySequenceTuple(f *os.File, i int) { + generateSequenceTuple1(identityHKT, "")(f, i) +} + +func generateIdentitySequenceT(f *os.File, i int) { + generateSequenceT1(identityHKT, "")(f, i) +} + +func generateIdentityHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) +`) + + for i := 1; i <= count; i++ { + // sequenceT + generateIdentitySequenceT(f, i) + // sequenceTuple + generateIdentitySequenceTuple(f, i) + // traverseTuple + generateIdentityTraverseTuple(f, i) + } + + return nil +} + +func IdentityCommand() *C.Command { + return &C.Command{ + Name: "identity", + Usage: "generate code for Identity", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateIdentityHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/io.go b/v2/cli/io.go new file mode 100644 index 0000000..a11a94e --- /dev/null +++ b/v2/cli/io.go @@ -0,0 +1,136 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + A "github.com/IBM/fp-go/v2/array" + C "github.com/urfave/cli/v2" +) + +func nonGenericIO(param string) string { + return fmt.Sprintf("IO[%s]", param) +} + +func genericIO(param string) string { + return fmt.Sprintf("func() %s", param) +} + +var extrasIO = A.Empty[string]() + +func generateIOSequenceT(f, fg *os.File, i int) { + generateGenericSequenceT(nonGenericIO, genericIO, extrasIO)(f, fg, i) +} + +func generateIOSequenceTuple(f, fg *os.File, i int) { + generateGenericSequenceTuple(nonGenericIO, genericIO, extrasIO)(f, fg, i) +} + +func generateIOTraverseTuple(f, fg *os.File, i int) { + generateGenericTraverseTuple(nonGenericIO, genericIO, extrasIO)(f, fg, i) +} + +func generateIOHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // construct subdirectory + genFilename := filepath.Join("generic", filename) + err = os.MkdirAll("generic", os.ModePerm) + if err != nil { + return err + } + fg, err := os.Create(filepath.Clean(genFilename)) + if err != nil { + return err + } + defer fg.Close() + + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + G "github.com/IBM/fp-go/v2/%s/generic" + T "github.com/IBM/fp-go/v2/tuple" +) +`, pkg) + + // some header + fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(fg, "// This file was generated by robots at") + fmt.Fprintf(fg, "// %s\n", time.Now()) + + fmt.Fprintf(fg, "package generic\n\n") + + fmt.Fprintf(fg, ` +import ( + T "github.com/IBM/fp-go/v2/tuple" + A "github.com/IBM/fp-go/v2/internal/apply" +) +`) + + for i := 1; i <= count; i++ { + // sequenceT + generateIOSequenceT(f, fg, i) + // sequenceTuple + generateIOSequenceTuple(f, fg, i) + // traverseTuple + generateIOTraverseTuple(f, fg, i) + } + + return nil +} + +func IOCommand() *C.Command { + return &C.Command{ + Name: "io", + Usage: "generate code for IO", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateIOHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/ioeither.go b/v2/cli/ioeither.go new file mode 100644 index 0000000..4470e88 --- /dev/null +++ b/v2/cli/ioeither.go @@ -0,0 +1,282 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + A "github.com/IBM/fp-go/v2/array" + C "github.com/urfave/cli/v2" +) + +// [GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GTAB ~func() ET.Either[E, T.Tuple2[A, B]], E, A, B any](a GA, b GB) GTAB { + +func nonGenericIOEither(param string) string { + return fmt.Sprintf("IOEither[E, %s]", param) +} + +func genericIOEither(param string) string { + return fmt.Sprintf("func() ET.Either[E, %s]", param) +} + +var extrasIOEither = A.From("E") + +func generateIOEitherSequenceT(f, fg *os.File, i int) { + generateGenericSequenceT(nonGenericIOEither, genericIOEither, extrasIOEither)(f, fg, i) +} + +func generateIOEitherSequenceTuple(f, fg *os.File, i int) { + generateGenericSequenceTuple(nonGenericIOEither, genericIOEither, extrasIOEither)(f, fg, i) +} + +func generateIOEitherTraverseTuple(f, fg *os.File, i int) { + generateGenericTraverseTuple(nonGenericIOEither, genericIOEither, extrasIOEither)(f, fg, i) +} + +func generateIOEitherUneitherize(f, fg *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [IOEither[error, R]]\n", i, i+1, i) + fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") IOEither[error, R]") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j+1) + } + fmt.Fprintf(f, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") (R, error) {\n") + fmt.Fprintf(f, " return G.Uneitherize%d[IOEither[error, R]](f)\n", i) + fmt.Fprintln(f, "}") + + // generic version + fmt.Fprintf(fg, "\n// Uneitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GIOA]\n", i, i, i) + fmt.Fprintf(fg, "func Uneitherize%d[GIOA ~func() ET.Either[error, R], GTA ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j+1) + } + fmt.Fprintf(fg, ") GIOA") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j+1) + } + fmt.Fprintf(fg, ", R any](f GTA) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j+1) + } + fmt.Fprintf(fg, ") (R, error) {\n") + fmt.Fprintf(fg, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "t%d T%d", j+1, j+1) + } + fmt.Fprintf(fg, ") (R, error) {\n") + fmt.Fprintf(fg, " return ET.Unwrap(f(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "t%d", j+1) + } + fmt.Fprintf(fg, ")())\n") + fmt.Fprintf(fg, " }\n") + fmt.Fprintf(fg, "}\n") +} + +func generateIOEitherEitherize(f, fg *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [IOEither[error, R]]\n", i, i+1, i) + fmt.Fprintf(f, "func Eitherize%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") (R, error)") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j+1) + } + fmt.Fprintf(f, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") IOEither[error, R] {\n") + fmt.Fprintf(f, " return G.Eitherize%d[IOEither[error, R]](f)\n", i) + fmt.Fprintln(f, "}") + + // generic version + fmt.Fprintf(fg, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GIOA]\n", i, i, i) + fmt.Fprintf(fg, "func Eitherize%d[GIOA ~func() ET.Either[error, R], F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j+1) + } + fmt.Fprintf(fg, ") (R, error)") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j+1) + } + fmt.Fprintf(fg, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j+1) + } + fmt.Fprintf(fg, ") GIOA {\n") + fmt.Fprintf(fg, " e := ET.Eitherize%d(f)\n", i) + fmt.Fprintf(fg, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "t%d T%d", j+1, j+1) + } + fmt.Fprintf(fg, ") GIOA {\n") + fmt.Fprintf(fg, " return func() ET.Either[error, R] {\n") + fmt.Fprintf(fg, " return e(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "t%d", j+1) + } + fmt.Fprintf(fg, ")\n") + fmt.Fprintf(fg, " }}\n") + fmt.Fprintf(fg, "}\n") +} + +func generateIOEitherHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // construct subdirectory + genFilename := filepath.Join("generic", filename) + err = os.MkdirAll("generic", os.ModePerm) + if err != nil { + return err + } + fg, err := os.Create(filepath.Clean(genFilename)) + if err != nil { + return err + } + defer fg.Close() + + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + G "github.com/IBM/fp-go/v2/%s/generic" + T "github.com/IBM/fp-go/v2/tuple" +) +`, pkg) + + // some header + fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(fg, "// This file was generated by robots at") + fmt.Fprintf(fg, "// %s\n", time.Now()) + + fmt.Fprintf(fg, "package generic\n\n") + + fmt.Fprintf(fg, ` +import ( + ET "github.com/IBM/fp-go/v2/either" + T "github.com/IBM/fp-go/v2/tuple" + A "github.com/IBM/fp-go/v2/internal/apply" +) +`) + + // eitherize + generateIOEitherEitherize(f, fg, 0) + // uneitherize + generateIOEitherUneitherize(f, fg, 0) + + for i := 1; i <= count; i++ { + // eitherize + generateIOEitherEitherize(f, fg, i) + // uneitherize + generateIOEitherUneitherize(f, fg, i) + // sequenceT + generateIOEitherSequenceT(f, fg, i) + // sequenceTuple + generateIOEitherSequenceTuple(f, fg, i) + // traverseTuple + generateIOEitherTraverseTuple(f, fg, i) + } + + return nil +} + +func IOEitherCommand() *C.Command { + return &C.Command{ + Name: "ioeither", + Usage: "generate code for IOEither", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateIOEitherHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/iooption.go b/v2/cli/iooption.go new file mode 100644 index 0000000..d44bda7 --- /dev/null +++ b/v2/cli/iooption.go @@ -0,0 +1,137 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + A "github.com/IBM/fp-go/v2/array" + C "github.com/urfave/cli/v2" +) + +func nonGenericIOOption(param string) string { + return fmt.Sprintf("IOOption[%s]", param) +} + +func genericIOOption(param string) string { + return fmt.Sprintf("func() O.Option[%s]", param) +} + +var extrasIOOption = A.Empty[string]() + +func generateIOOptionSequenceT(f, fg *os.File, i int) { + generateGenericSequenceT(nonGenericIOOption, genericIOOption, extrasIOOption)(f, fg, i) +} + +func generateIOOptionSequenceTuple(f, fg *os.File, i int) { + generateGenericSequenceTuple(nonGenericIOOption, genericIOOption, extrasIOOption)(f, fg, i) +} + +func generateIOOptionTraverseTuple(f, fg *os.File, i int) { + generateGenericTraverseTuple(nonGenericIOOption, genericIOOption, extrasIOOption)(f, fg, i) +} + +func generateIOOptionHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // construct subdirectory + genFilename := filepath.Join("generic", filename) + err = os.MkdirAll("generic", os.ModePerm) + if err != nil { + return err + } + fg, err := os.Create(filepath.Clean(genFilename)) + if err != nil { + return err + } + defer fg.Close() + + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + G "github.com/IBM/fp-go/v2/%s/generic" + T "github.com/IBM/fp-go/v2/tuple" +) +`, pkg) + + // some header + fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(fg, "// This file was generated by robots at") + fmt.Fprintf(fg, "// %s\n", time.Now()) + + fmt.Fprintf(fg, "package generic\n\n") + + fmt.Fprintf(fg, ` +import ( + T "github.com/IBM/fp-go/v2/tuple" + O "github.com/IBM/fp-go/v2/option" + A "github.com/IBM/fp-go/v2/internal/apply" +) +`) + + for i := 1; i <= count; i++ { + // sequenceT + generateIOOptionSequenceT(f, fg, i) + // sequenceTuple + generateIOOptionSequenceTuple(f, fg, i) + // traverseTuple + generateIOOptionTraverseTuple(f, fg, i) + } + + return nil +} + +func IOOptionCommand() *C.Command { + return &C.Command{ + Name: "iooption", + Usage: "generate code for IOOption", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateIOOptionHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/monad.go b/v2/cli/monad.go new file mode 100644 index 0000000..e2b0a99 --- /dev/null +++ b/v2/cli/monad.go @@ -0,0 +1,359 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "os" + "strings" +) + +func tupleType(name string) func(i int) string { + return func(i int) string { + var buf strings.Builder + buf.WriteString(fmt.Sprintf("T.Tuple%d[", i)) + for j := 0; j < i; j++ { + if j > 0 { + buf.WriteString(", ") + } + buf.WriteString(fmt.Sprintf("%s%d", name, j+1)) + } + buf.WriteString("]") + + return buf.String() + } +} + +func monadGenerateSequenceTNonGeneric( + hkt func(string) string, + fmap func(string, string) string, + fap func(string, string) string, +) func(f *os.File, i int) { + return func(f *os.File, i int) { + + tuple := tupleType("T")(i) + + fmt.Fprintf(f, "SequenceT%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "](") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(f, ") %s {", hkt(tuple)) + // the actual apply callback + fmt.Fprintf(f, " return apply.SequenceT%d(\n", i) + // map callback + + curried := func(count int) string { + var buf strings.Builder + for j := count; j < i; j++ { + buf.WriteString(fmt.Sprintf("func(T%d)", j+1)) + } + buf.WriteString(tuple) + return buf.String() + } + + fmt.Fprintf(f, " %s,\n", fmap("T1", curried(1))) + for j := 1; j < i; j++ { + fmt.Fprintf(f, " %s,\n", fap(curried(j+1), fmt.Sprintf("T%d", j))) + } + + for j := 0; j < i; j++ { + fmt.Fprintf(f, " T%d,\n", j+1) + } + + fmt.Fprintf(f, " )\n") + + fmt.Fprintf(f, "}\n") + + } +} + +func monadGenerateSequenceTGeneric( + hkt func(string) string, + fmap func(string, string) string, + fap func(string, string) string, +) func(f *os.File, i int) { + return func(f *os.File, i int) { + + tuple := tupleType("T")(i) + + fmt.Fprintf(f, "SequenceT%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "](") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(f, ") %s {", hkt(tuple)) + // the actual apply callback + fmt.Fprintf(f, " return apply.SequenceT%d(\n", i) + // map callback + + curried := func(count int) string { + var buf strings.Builder + for j := count; j < i; j++ { + buf.WriteString(fmt.Sprintf("func(T%d)", j+1)) + } + buf.WriteString(tuple) + return buf.String() + } + + fmt.Fprintf(f, " %s,\n", fmap("T1", curried(1))) + for j := 1; j < i; j++ { + fmt.Fprintf(f, " %s,\n", fap(curried(j+1), fmt.Sprintf("T%d", j))) + } + + for j := 0; j < i; j++ { + fmt.Fprintf(f, " T%d,\n", j+1) + } + + fmt.Fprintf(f, " )\n") + + fmt.Fprintf(f, "}\n") + + } +} + +func generateTraverseTuple1( + hkt func(string) string, + infix string) func(f *os.File, i int) { + + return func(f *os.File, i int) { + tuple := tupleType("T")(i) + + fmt.Fprintf(f, "\n// TraverseTuple%d converts a [Tuple%d] of [A] via transformation functions transforming [A] to [%s] into a [%s].\n", i, i, hkt("A"), hkt(fmt.Sprintf("Tuple%d", i))) + fmt.Fprintf(f, "func TraverseTuple%d[", i) + // functions + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "F%d ~func(A%d) %s", j+1, j+1, hkt(fmt.Sprintf("T%d", j+1))) + } + if infix != "" { + fmt.Fprintf(f, ", %s", infix) + } + // types + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", A%d, T%d", j+1, j+1) + } + fmt.Fprintf(f, " any](") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d F%d", j+1, j+1) + } + fmt.Fprintf(f, ") func (T.Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "A%d", j+1) + } + fmt.Fprintf(f, "]) %s {\n", hkt(tuple)) + fmt.Fprintf(f, " return func(t T.Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "A%d", j+1) + } + fmt.Fprintf(f, "]) %s {\n", hkt(tuple)) + fmt.Fprintf(f, " return A.TraverseTuple%d(\n", i) + // map + fmt.Fprintf(f, " Map[") + if infix != "" { + fmt.Fprintf(f, "%s, T1,", infix) + } else { + fmt.Fprintf(f, "T1,") + } + for j := 1; j < i; j++ { + fmt.Fprintf(f, " func(T%d)", j+1) + } + fmt.Fprintf(f, " %s],\n", tuple) + // applicatives + for j := 1; j < i; j++ { + fmt.Fprintf(f, " Ap[") + for k := j + 1; k < i; k++ { + if k > j+1 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "func(T%d)", k+1) + } + if j < i-1 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "%s", tuple) + if infix != "" { + fmt.Fprintf(f, ", %s", infix) + } + fmt.Fprintf(f, ", T%d],\n", j+1) + } + for j := 0; j < i; j++ { + fmt.Fprintf(f, " f%d,\n", j+1) + } + fmt.Fprintf(f, " t,\n") + fmt.Fprintf(f, " )\n") + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, "}\n") + } +} + +func generateSequenceTuple1( + hkt func(string) string, + infix string) func(f *os.File, i int) { + + return func(f *os.File, i int) { + + tuple := tupleType("T")(i) + + fmt.Fprintf(f, "\n// SequenceTuple%d converts a [Tuple%d] of [%s] into an [%s].\n", i, i, hkt("T"), hkt(fmt.Sprintf("Tuple%d", i))) + fmt.Fprintf(f, "func SequenceTuple%d[", i) + if infix != "" { + fmt.Fprintf(f, "%s", infix) + } + for j := 0; j < i; j++ { + if infix != "" || j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any](t T.Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "%s", hkt(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(f, "]) %s {\n", hkt(tuple)) + fmt.Fprintf(f, " return A.SequenceTuple%d(\n", i) + // map + fmt.Fprintf(f, " Map[") + if infix != "" { + fmt.Fprintf(f, "%s, T1,", infix) + } else { + fmt.Fprintf(f, "T1,") + } + for j := 1; j < i; j++ { + fmt.Fprintf(f, " func(T%d)", j+1) + } + fmt.Fprintf(f, " %s],\n", tuple) + // applicatives + for j := 1; j < i; j++ { + fmt.Fprintf(f, " Ap[") + for k := j + 1; k < i; k++ { + if k > j+1 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "func(T%d)", k+1) + } + if j < i-1 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "%s", tuple) + if infix != "" { + fmt.Fprintf(f, ", %s", infix) + } + fmt.Fprintf(f, ", T%d],\n", j+1) + } + fmt.Fprintf(f, " t,\n") + fmt.Fprintf(f, " )\n") + fmt.Fprintf(f, "}\n") + } +} + +func generateSequenceT1( + hkt func(string) string, + infix string) func(f *os.File, i int) { + + return func(f *os.File, i int) { + + tuple := tupleType("T")(i) + + fmt.Fprintf(f, "\n// SequenceT%d converts %d parameters of [%s] into a [%s].\n", i, i, hkt("T"), hkt(fmt.Sprintf("Tuple%d", i))) + fmt.Fprintf(f, "func SequenceT%d[", i) + if infix != "" { + fmt.Fprintf(f, "%s", infix) + } + for j := 0; j < i; j++ { + if infix != "" || j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any](") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d %s", j+1, hkt(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(f, ") %s {\n", hkt(tuple)) + fmt.Fprintf(f, " return A.SequenceT%d(\n", i) + // map + fmt.Fprintf(f, " Map[") + if infix != "" { + fmt.Fprintf(f, "%s, T1,", infix) + } else { + fmt.Fprintf(f, "T1,") + } + for j := 1; j < i; j++ { + fmt.Fprintf(f, " func(T%d)", j+1) + } + fmt.Fprintf(f, " %s],\n", tuple) + // applicatives + for j := 1; j < i; j++ { + fmt.Fprintf(f, " Ap[") + for k := j + 1; k < i; k++ { + if k > j+1 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "func(T%d)", k+1) + } + if j < i-1 { + fmt.Fprintf(f, " ") + } + fmt.Fprintf(f, "%s", tuple) + if infix != "" { + fmt.Fprintf(f, ", %s", infix) + } + fmt.Fprintf(f, ", T%d],\n", j+1) + } + for j := 0; j < i; j++ { + fmt.Fprintf(f, " t%d,\n", j+1) + } + fmt.Fprintf(f, " )\n") + fmt.Fprintf(f, "}\n") + } + +} diff --git a/v2/cli/monad2.go b/v2/cli/monad2.go new file mode 100644 index 0000000..aec143f --- /dev/null +++ b/v2/cli/monad2.go @@ -0,0 +1,284 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "os" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" + S "github.com/IBM/fp-go/v2/string" +) + +var ( + concStrgs = A.Monoid[string]().Concat + intercalStrgs = A.Intercalate(S.Monoid) + concAllStrgs = A.ConcatAll(A.Monoid[string]()) +) + +func joinAll(middle string) func(all ...[]string) string { + ic := intercalStrgs(middle) + return func(all ...[]string) string { + return ic(concAllStrgs(all)) + } +} + +func generateGenericSequenceT( + nonGenericType func(string) string, + genericType func(string) string, + extra []string, +) func(f, fg *os.File, i int) { + return func(f, fg *os.File, i int) { + // tuple + tuple := tupleType("T")(i) + // all types T + typesT := A.MakeBy(i, F.Flow2( + N.Inc[int], + S.Format[int]("T%d"), + )) + // non generic version + fmt.Fprintf(f, "\n// SequenceT%d converts %d [%s] into a [%s]\n", i, i, nonGenericType("T"), nonGenericType(tuple)) + fmt.Fprintf(f, "func SequenceT%d[%s any](\n", i, joinAll(", ")(extra, typesT)) + for j := 0; j < i; j++ { + fmt.Fprintf(f, " t%d %s,\n", j+1, nonGenericType(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(f, ") %s {\n", nonGenericType(tuple)) + fmt.Fprintf(f, " return G.SequenceT%d[\n", i) + fmt.Fprintf(f, " %s,\n", nonGenericType(tuple)) + for j := 0; j < i; j++ { + fmt.Fprintf(f, " %s,\n", nonGenericType(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(f, " ](") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j+1) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") + + // generic version + fmt.Fprintf(fg, "\n// SequenceT%d converts %d [%s] into a [%s]\n", i, i, genericType("T"), genericType(tuple)) + fmt.Fprintf(fg, "func SequenceT%d[\n", i) + fmt.Fprintf(fg, " G_TUPLE%d ~%s,\n", i, genericType(tuple)) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " G_T%d ~%s, \n", j+1, genericType(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(fg, " %s any](\n", joinAll(", ")(extra, typesT)) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " t%d G_T%d,\n", j+1, j+1) + } + fmt.Fprintf(fg, ") G_TUPLE%d {\n", i) + fmt.Fprintf(fg, " return A.SequenceT%d(\n", i) + // map call + var cio string + cb := generateNestedCallbacks(1, i) + if i > 1 { + cio = genericType(cb) + } else { + cio = fmt.Sprintf("G_TUPLE%d", i) + } + fmt.Fprintf(fg, " Map[%s],\n", joinAll(", ")(A.From("G_T1", cio), extra, A.From("T1", cb))) + // the apply calls + for j := 1; j < i; j++ { + if j < i-1 { + cb := generateNestedCallbacks(j+1, i) + cio = genericType(cb) + } else { + cio = fmt.Sprintf("G_TUPLE%d", i) + } + fmt.Fprintf(fg, " Ap[%s, %s, G_T%d],\n", cio, genericType(generateNestedCallbacks(j, i)), j+1) + } + // function parameters + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " t%d,\n", j+1) + } + + fmt.Fprintf(fg, " )\n") + fmt.Fprintf(fg, "}\n") + } +} + +func generateGenericSequenceTuple( + nonGenericType func(string) string, + genericType func(string) string, + extra []string, +) func(f, fg *os.File, i int) { + return func(f, fg *os.File, i int) { + // tuple + tuple := tupleType("T")(i) + // all types T + typesT := A.MakeBy(i, F.Flow2( + N.Inc[int], + S.Format[int]("T%d"), + )) + // non generic version + fmt.Fprintf(f, "\n// SequenceTuple%d converts a [T.Tuple%d[%s]] into a [%s]\n", i, i, nonGenericType("T"), nonGenericType(tuple)) + fmt.Fprintf(f, "func SequenceTuple%d[%s any](t T.Tuple%d[", i, joinAll(", ")(extra, typesT), i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "%s", nonGenericType(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(f, "]) %s {\n", nonGenericType(tuple)) + fmt.Fprintf(f, " return G.SequenceTuple%d[\n", i) + fmt.Fprintf(f, " %s,\n", nonGenericType(tuple)) + for j := 0; j < i; j++ { + fmt.Fprintf(f, " %s,\n", nonGenericType(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(f, " ](t)\n") + fmt.Fprintf(f, "}\n") + + // generic version + fmt.Fprintf(fg, "\n// SequenceTuple%d converts a [T.Tuple%d[%s]] into a [%s]\n", i, i, genericType("T"), genericType(tuple)) + fmt.Fprintf(fg, "func SequenceTuple%d[\n", i) + fmt.Fprintf(fg, " G_TUPLE%d ~%s,\n", i, genericType(tuple)) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " G_T%d ~%s, \n", j+1, genericType(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(fg, " %s any](t T.Tuple%d[", joinAll(", ")(extra, typesT), i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "G_T%d", j+1) + } + fmt.Fprintf(fg, "]) G_TUPLE%d {\n", i) + fmt.Fprintf(fg, " return A.SequenceTuple%d(\n", i) + // map call + var cio string + cb := generateNestedCallbacks(1, i) + if i > 1 { + cio = genericType(cb) + } else { + cio = fmt.Sprintf("G_TUPLE%d", i) + } + fmt.Fprintf(fg, " Map[%s],\n", joinAll(", ")(A.From("G_T1", cio), extra, A.From("T1", cb))) + // the apply calls + for j := 1; j < i; j++ { + if j < i-1 { + cb := generateNestedCallbacks(j+1, i) + cio = genericType(cb) + } else { + cio = fmt.Sprintf("G_TUPLE%d", i) + } + fmt.Fprintf(fg, " Ap[%s, %s, G_T%d],\n", cio, genericType(generateNestedCallbacks(j, i)), j+1) + } + // function parameters + fmt.Fprintf(fg, " t)\n") + fmt.Fprintf(fg, "}\n") + } +} + +func generateGenericTraverseTuple( + nonGenericType func(string) string, + genericType func(string) string, + extra []string, +) func(f, fg *os.File, i int) { + return func(f, fg *os.File, i int) { + // tuple + tupleT := tupleType("T")(i) + tupleA := tupleType("A")(i) + // all types T + typesT := A.MakeBy(i, F.Flow2( + N.Inc[int], + S.Format[int]("T%d"), + )) + // all types A + typesA := A.MakeBy(i, F.Flow2( + N.Inc[int], + S.Format[int]("A%d"), + )) + // all function types + typesF := A.MakeBy(i, F.Flow2( + N.Inc[int], + func(j int) string { + return fmt.Sprintf("F%d ~func(A%d) %s", j, j, nonGenericType(fmt.Sprintf("T%d", j))) + }, + )) + // non generic version + fmt.Fprintf(f, "\n// TraverseTuple%d converts a [T.Tuple%d[%s]] into a [%s]\n", i, i, nonGenericType("T"), nonGenericType(tupleT)) + fmt.Fprintf(f, "func TraverseTuple%d[%s any](", i, joinAll(", ")(typesF, extra, typesA, typesT)) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d F%d", j+1, j+1) + } + fmt.Fprintf(f, ") func(%s) %s {\n", tupleA, nonGenericType(tupleT)) + fmt.Fprintf(f, " return G.TraverseTuple%d[%s](", i, nonGenericType(tupleT)) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d", j+1) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") + + // generic version + fmt.Fprintf(fg, "\n// TraverseTuple%d converts a [T.Tuple%d[%s]] into a [%s]\n", i, i, genericType("T"), genericType(tupleT)) + fmt.Fprintf(fg, "func TraverseTuple%d[\n", i) + fmt.Fprintf(fg, " G_TUPLE%d ~%s,\n", i, genericType(tupleT)) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " F%d ~func(A%d) G_T%d,\n", j+1, j+1, j+1) + } + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " G_T%d ~%s, \n", j+1, genericType(fmt.Sprintf("T%d", j+1))) + } + fmt.Fprintf(fg, " %s any](", joinAll(", ")(extra, typesA, typesT)) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "f%d F%d", j+1, j+1) + } + fmt.Fprintf(fg, ") func(%s) G_TUPLE%d {\n", tupleA, i) + fmt.Fprintf(fg, " return func(t %s) G_TUPLE%d {\n", tupleA, i) + fmt.Fprintf(fg, " return A.TraverseTuple%d(\n", i) + // map call + var cio string + cb := generateNestedCallbacks(1, i) + if i > 1 { + cio = genericType(cb) + } else { + cio = fmt.Sprintf("G_TUPLE%d", i) + } + fmt.Fprintf(fg, " Map[%s],\n", joinAll(", ")(A.From("G_T1", cio), extra, A.From("T1", cb))) + // the apply calls + for j := 1; j < i; j++ { + if j < i-1 { + cb := generateNestedCallbacks(j+1, i) + cio = genericType(cb) + } else { + cio = fmt.Sprintf("G_TUPLE%d", i) + } + fmt.Fprintf(fg, " Ap[%s, %s, G_T%d],\n", cio, genericType(generateNestedCallbacks(j, i)), j+1) + } + // function parameters + for j := 0; j < i; j++ { + fmt.Fprintf(fg, " f%d,\n", j+1) + } + // tuple parameter + fmt.Fprintf(fg, " t)\n") + fmt.Fprintf(fg, " }\n") + fmt.Fprintf(fg, "}\n") + } +} diff --git a/v2/cli/option.go b/v2/cli/option.go new file mode 100644 index 0000000..fb21d11 --- /dev/null +++ b/v2/cli/option.go @@ -0,0 +1,210 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func optionHKT(typeA string) string { + return fmt.Sprintf("Option[%s]", typeA) +} + +func generateOptionTraverseTuple(f *os.File, i int) { + generateTraverseTuple1(optionHKT, "")(f, i) +} + +func generateOptionSequenceTuple(f *os.File, i int) { + generateSequenceTuple1(optionHKT, "")(f, i) +} + +func generateOptionSequenceT(f *os.File, i int) { + generateSequenceT1(optionHKT, "")(f, i) +} + +func generateOptionize(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Optionize%d converts a function with %d parameters returning a tuple of a return value R and a boolean into a function with %d parameters returning an Option[R]\n", i, i, i) + fmt.Fprintf(f, "func Optionize%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") (R, bool)") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") Option[R] {\n") + fmt.Fprintf(f, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j, j) + } + fmt.Fprintf(f, ") Option[R] {\n") + fmt.Fprintf(f, " return optionize(func() (R, bool) {\n") + fmt.Fprintf(f, " return f(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j) + } + fmt.Fprintln(f, ")") + fmt.Fprintln(f, " })") + fmt.Fprintln(f, " }") + fmt.Fprintln(f, "}") +} + +func generateUnoptionize(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Unoptionize%d converts a function with %d parameters returning a tuple of a return value R and a boolean into a function with %d parameters returning an Option[R]\n", i, i, i) + fmt.Fprintf(f, "func Unoptionize%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") Option[R]") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") (R, bool) {\n") + fmt.Fprintf(f, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j, j) + } + fmt.Fprintf(f, ") (R, bool) {\n") + fmt.Fprintf(f, " return Unwrap(f(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j) + } + fmt.Fprintln(f, "))") + fmt.Fprintln(f, " }") + fmt.Fprintln(f, "}") +} + +func generateOptionHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) +`) + + // print out some helpers + fmt.Fprintf(f, `// optionize converts a nullary function to an option +func optionize[R any](f func() (R, bool)) Option[R] { + if r, ok := f(); ok { + return Some(r) + } + return None[R]() +} +`) + + // zero level functions + + // optionize + generateOptionize(f, 0) + // unoptionize + generateUnoptionize(f, 0) + + for i := 1; i <= count; i++ { + // optionize + generateOptionize(f, i) + // unoptionize + generateUnoptionize(f, i) + // sequenceT + generateOptionSequenceT(f, i) + // sequenceTuple + generateOptionSequenceTuple(f, i) + // traverseTuple + generateOptionTraverseTuple(f, i) + } + + return nil +} + +func OptionCommand() *C.Command { + return &C.Command{ + Name: "option", + Usage: "generate code for Option", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateOptionHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/pipe.go b/v2/cli/pipe.go new file mode 100644 index 0000000..6af857b --- /dev/null +++ b/v2/cli/pipe.go @@ -0,0 +1,433 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func generateUnsliced(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Unsliced%d converts a function taking a slice parameter into a function with %d parameters\n", i, i) + fmt.Fprintf(f, "func Unsliced%d[F ~func([]T) R, T, R any](f F) func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T") + } + fmt.Fprintf(f, ") R {\n") + fmt.Fprintf(f, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j+1) + } + if i > 0 { + fmt.Fprintf(f, " T") + } + fmt.Fprintf(f, ") R {\n") + fmt.Fprintf(f, " return f([]T{") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j+1) + } + fmt.Fprintln(f, "})") + fmt.Fprintln(f, " }") + fmt.Fprintln(f, "}") +} + +func generateVariadic(f *os.File, i int) { + // Create the nullary version + fmt.Fprintf(f, "\n// Variadic%d converts a function taking %d parameters and a final slice into a function with %d parameters but a final variadic argument\n", i, i, i) + fmt.Fprintf(f, "func Variadic%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "V, R any](f func(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "[]V) R) func(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "...V) R {\n") + fmt.Fprintf(f, " return func(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j, j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "v ...V) R {\n") + fmt.Fprintf(f, " return f(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "v)\n") + fmt.Fprintf(f, " }\n") + + fmt.Fprintf(f, "}\n") +} + +func generateUnvariadic(f *os.File, i int) { + // Create the nullary version + fmt.Fprintf(f, "\n// Unvariadic%d converts a function taking %d parameters and a final variadic argument into a function with %d parameters but a final slice argument\n", i, i, i) + fmt.Fprintf(f, "func Unvariadic%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "V, R any](f func(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "...V) R) func(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "[]V) R {\n") + fmt.Fprintf(f, " return func(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j, j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "v []V) R {\n") + fmt.Fprintf(f, " return f(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "v...)\n") + fmt.Fprintf(f, " }\n") + + fmt.Fprintf(f, "}\n") +} + +func generateNullary(f *os.File, i int) { + // Create the nullary version + fmt.Fprintf(f, "\n// Nullary%d creates a parameter less function from a parameter less function and %d functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions\n", i, i-1) + fmt.Fprintf(f, "func Nullary%d[F1 ~func() T1", i) + for j := 2; j <= i; j++ { + fmt.Fprintf(f, ", F%d ~func(T%d) T%d", j, j-1, j) + } + for j := 1; j <= i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, " any](f1 F1") + for j := 2; j <= i; j++ { + fmt.Fprintf(f, ", f%d F%d", j, j) + } + fmt.Fprintf(f, ") func() T%d {\n", i) + fmt.Fprintf(f, " return func() T%d {\n", i) + fmt.Fprintf(f, " return Pipe%d(f1()", i-1) + for j := 2; j <= i; j++ { + fmt.Fprintf(f, ", f%d", j) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintln(f, " }") + + fmt.Fprintln(f, "}") +} + +func generateFlow(f *os.File, i int) { + // Create the flow version + fmt.Fprintf(f, "\n// Flow%d creates a function that takes an initial value t0 and successively applies %d functions where the input of a function is the return value of the previous function\n// The final return value is the result of the last function application\n", i, i) + fmt.Fprintf(f, "func Flow%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "F%d ~func(T%d) T%d", j, j-1, j) + } + for j := 0; j <= i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, " any](") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d F%d", j, j) + } + fmt.Fprintf(f, ") func(T0) T%d {\n", i) + fmt.Fprintf(f, " return func(t0 T0) T%d {\n", i) + fmt.Fprintf(f, " return Pipe%d(t0", i) + for j := 1; j <= i; j++ { + fmt.Fprintf(f, ", f%d", j) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintln(f, " }") + + fmt.Fprintln(f, "}") + +} + +func generatePipe(f *os.File, i int) { + // Create the pipe version + fmt.Fprintf(f, "\n// Pipe%d takes an initial value t0 and successively applies %d functions where the input of a function is the return value of the previous function\n// The final return value is the result of the last function application\n", i, i) + fmt.Fprintf(f, "func Pipe%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "F%d ~func(T%d) T%d", j, j-1, j) + } + if i > 0 { + fmt.Fprintf(f, ", ") + } + for j := 0; j <= i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + + fmt.Fprintf(f, " any](t0 T0") + for j := 1; j <= i; j++ { + fmt.Fprintf(f, ", f%d F%d", j, j) + } + fmt.Fprintf(f, ") T%d {\n", i) + fmt.Fprintf(f, " return ") + for j := i; j >= 1; j-- { + fmt.Fprintf(f, "f%d(", j) + } + fmt.Fprintf(f, "t0") + for j := 1; j <= i; j++ { + fmt.Fprintf(f, ")") + } + fmt.Fprintf(f, "\n") + fmt.Fprintln(f, "}") +} + +func recurseCurry(f *os.File, indent string, total, count int) { + if count == 1 { + fmt.Fprintf(f, "%sreturn func(t%d T%d) T%d {\n", indent, total-1, total-1, total) + fmt.Fprintf(f, "%s return f(t0", indent) + for i := 1; i < total; i++ { + fmt.Fprintf(f, ", t%d", i) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "%s}\n", indent) + } else { + fmt.Fprintf(f, "%sreturn", indent) + for i := total - count + 1; i <= total; i++ { + fmt.Fprintf(f, " func(t%d T%d)", i-1, i-1) + } + fmt.Fprintf(f, " T%d {\n", total) + recurseCurry(f, fmt.Sprintf(" %s", indent), total, count-1) + fmt.Fprintf(f, "%s}\n", indent) + } +} + +func generateCurry(f *os.File, i int) { + // Create the curry version + fmt.Fprintf(f, "\n// Curry%d takes a function with %d parameters and returns a cascade of functions each taking only one parameter.\n// The inverse function is [Uncurry%d]\n", i, i, i) + fmt.Fprintf(f, "func Curry%d[FCT ~func(T0", i) + for j := 1; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ") T%d", i) + // type arguments + for j := 0; j <= i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, " any](f FCT) func(T0)") + for j := 2; j <= i; j++ { + fmt.Fprintf(f, " func(T%d)", j-1) + } + fmt.Fprintf(f, " T%d {\n", i) + recurseCurry(f, " ", i, i) + fmt.Fprintf(f, "}\n") +} + +func generateUncurry(f *os.File, i int) { + // Create the uncurry version + fmt.Fprintf(f, "\n// Uncurry%d takes a cascade of %d functions each taking only one parameter and returns a function with %d parameters .\n// The inverse function is [Curry%d]\n", i, i, i, i) + fmt.Fprintf(f, "func Uncurry%d[FCT ~func(T0)", i) + for j := 1; j < i; j++ { + fmt.Fprintf(f, " func(T%d)", j) + } + fmt.Fprintf(f, " T%d", i) + // the type parameters + for j := 0; j <= i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, " any](f FCT) func(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j-1) + } + fmt.Fprintf(f, ") T%d {\n", i) + fmt.Fprintf(f, " return func(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j-1, j-1) + } + fmt.Fprintf(f, ") T%d {\n", i) + fmt.Fprintf(f, " return f") + for j := 1; j <= i; j++ { + fmt.Fprintf(f, "(t%d)", j-1) + } + fmt.Fprintln(f) + + fmt.Fprintf(f, " }\n") + + fmt.Fprintf(f, "}\n") +} + +func generatePipeHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n", pkg) + + // pipe + generatePipe(f, 0) + // variadic + generateVariadic(f, 0) + // unvariadic + generateUnvariadic(f, 0) + // unsliced + generateUnsliced(f, 0) + + for i := 1; i <= count; i++ { + + // pipe + generatePipe(f, i) + // flow + generateFlow(f, i) + // nullary + generateNullary(f, i) + // curry + generateCurry(f, i) + // uncurry + generateUncurry(f, i) + // variadic + generateVariadic(f, i) + // unvariadic + generateUnvariadic(f, i) + // unsliced + generateUnsliced(f, i) + } + + return nil +} + +func PipeCommand() *C.Command { + return &C.Command{ + Name: "pipe", + Usage: "generate code for pipe, flow, curry, etc", + Description: "Code generation for pipe, flow, curry, etc", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generatePipeHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/reader.go b/v2/cli/reader.go new file mode 100644 index 0000000..590d16b --- /dev/null +++ b/v2/cli/reader.go @@ -0,0 +1,164 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func generateReaderFrom(f, fg *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// From%d converts a function with %d parameters returning a [R] into a function with %d parameters returning a [Reader[C, R]]\n// The first parameter is considered to be the context [C] of the reader\n", i, i+1, i) + fmt.Fprintf(f, "func From%d[F ~func(C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ") R") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", C, R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") Reader[C, R] {\n") + fmt.Fprintf(f, " return G.From%d[Reader[C, R]](f)\n", i) + fmt.Fprintln(f, "}") + + // generic version + fmt.Fprintf(fg, "\n// From%d converts a function with %d parameters returning a [R] into a function with %d parameters returning a [GRA]\n// The first parameter is considered to be the context [C].\n", i, i+1, i) + fmt.Fprintf(fg, "func From%d[GRA ~func(C) R, F ~func(C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ") R") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ", C, R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j) + } + fmt.Fprintf(fg, ") GRA {\n") + + fmt.Fprintf(fg, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "t%d T%d", j, j) + } + fmt.Fprintf(fg, ") GRA {\n") + fmt.Fprintf(fg, " return MakeReader[GRA](func(r C) R {\n") + fmt.Fprintf(fg, " return f(r") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", t%d", j) + } + fmt.Fprintf(fg, ")\n") + fmt.Fprintf(fg, " })\n") + fmt.Fprintf(fg, " }\n") + fmt.Fprintf(fg, "}\n") +} + +func generateReaderHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // construct subdirectory + genFilename := filepath.Join("generic", filename) + err = os.MkdirAll("generic", os.ModePerm) + if err != nil { + return err + } + fg, err := os.Create(filepath.Clean(genFilename)) + if err != nil { + return err + } + defer fg.Close() + + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + G "github.com/IBM/fp-go/v2/%s/generic" +) +`, pkg) + + // some header + fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(fg, "// This file was generated by robots at") + fmt.Fprintf(fg, "// %s\n", time.Now()) + + fmt.Fprintf(fg, "package generic\n\n") + + // from + generateReaderFrom(f, fg, 0) + + for i := 1; i <= count; i++ { + // from + generateReaderFrom(f, fg, i) + } + + return nil +} + +func ReaderCommand() *C.Command { + return &C.Command{ + Name: "reader", + Usage: "generate code for Reader", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateReaderHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/readerioeither.go b/v2/cli/readerioeither.go new file mode 100644 index 0000000..e1c1a32 --- /dev/null +++ b/v2/cli/readerioeither.go @@ -0,0 +1,294 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" + + C "github.com/urfave/cli/v2" +) + +func generateReaderIOEitherFrom(f, fg *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// From%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[R]]\n// The first parameter is considered to be the context [C].\n", i, i+1, i) + fmt.Fprintf(f, "func From%d[F ~func(C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ") func() (R, error)") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", C, R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") ReaderIOEither[C, error, R] {\n") + fmt.Fprintf(f, " return G.From%d[ReaderIOEither[C, error, R]](f)\n", i) + fmt.Fprintln(f, "}") + + // generic version + fmt.Fprintf(fg, "\n// From%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The first parameter is considerd to be the context [C].\n", i, i+1, i) + fmt.Fprintf(fg, "func From%d[GRA ~func(C) GIOA, F ~func(C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ") func() (R, error), GIOA ~func() E.Either[error, R]") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ", C, R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j) + } + fmt.Fprintf(fg, ") GRA {\n") + + fmt.Fprintf(fg, " return RD.From%d[GRA](func(r C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", t%d T%d", j, j) + } + fmt.Fprintf(fg, ") GIOA {\n") + fmt.Fprintf(fg, " return E.Eitherize0(f(r") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", t%d", j) + } + fmt.Fprintf(fg, "))\n") + fmt.Fprintf(fg, " })\n") + fmt.Fprintf(fg, "}\n") +} + +func generateReaderIOEitherEitherize(f, fg *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [ReaderIOEither[C, error, R]]\n// The first parameter is considered to be the context [C].\n", i, i+1, i) + fmt.Fprintf(f, "func Eitherize%d[F ~func(C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ") (R, error)") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", C, R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") ReaderIOEither[C, error, R] {\n") + fmt.Fprintf(f, " return G.Eitherize%d[ReaderIOEither[C, error, R]](f)\n", i) + fmt.Fprintln(f, "}") + + // generic version + fmt.Fprintf(fg, "\n// Eitherize%d converts a function with %d parameters returning a tuple into a function with %d parameters returning a [GRA]\n// The first parameter is considered to be the context [C].\n", i, i, i) + fmt.Fprintf(fg, "func Eitherize%d[GRA ~func(C) GIOA, F ~func(C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ", C, R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j) + } + fmt.Fprintf(fg, ") GRA {\n") + + fmt.Fprintf(fg, " return From%d[GRA](func(r C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", t%d T%d", j, j) + } + fmt.Fprintf(fg, ") func() (R, error) {\n") + fmt.Fprintf(fg, " return func() (R, error) {\n") + fmt.Fprintf(fg, " return f(r") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", t%d", j) + } + fmt.Fprintf(fg, ")\n") + fmt.Fprintf(fg, " }})\n") + fmt.Fprintf(fg, "}\n") +} + +func generateReaderIOEitherUneitherize(f, fg *os.File, i int) { + // non generic version + fmt.Fprintf(f, "\n// Uneitherize%d converts a function with %d parameters returning a [ReaderIOEither[C, error, R]] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the context [C].\n", i, i+1, i) + fmt.Fprintf(f, "func Uneitherize%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, ") ReaderIOEither[C, error, R]") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", C, R any](f F) func(C") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ") (R, error) {\n") + fmt.Fprintf(f, " return G.Uneitherize%d[ReaderIOEither[C, error, R]", i) + + fmt.Fprintf(f, ", func(C") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ")(R, error)](f)\n") + fmt.Fprintln(f, "}") + + // generic version + fmt.Fprintf(fg, "\n// Uneitherize%d converts a function with %d parameters returning a [GRA] into a function with %d parameters returning a tuple.\n// The first parameter is considered to be the context [C].\n", i, i, i) + fmt.Fprintf(fg, "func Uneitherize%d[GRA ~func(C) GIOA, F ~func(C", i) + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ") (R, error), GIOA ~func() E.Either[error, R]") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", T%d", j) + } + fmt.Fprintf(fg, ", C, R any](f func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "T%d", j) + } + fmt.Fprintf(fg, ") GRA) F {\n") + + fmt.Fprintf(fg, " return func(c C") + for j := 0; j < i; j++ { + fmt.Fprintf(fg, ", t%d T%d", j, j) + } + fmt.Fprintf(fg, ") (R, error) {\n") + fmt.Fprintf(fg, " return E.UnwrapError(f(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(fg, ", ") + } + fmt.Fprintf(fg, "t%d", j) + } + fmt.Fprintf(fg, ")(c)())\n") + fmt.Fprintf(fg, " }\n") + fmt.Fprintf(fg, "}\n") +} + +func generateReaderIOEitherHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // construct subdirectory + genFilename := filepath.Join("generic", filename) + err = os.MkdirAll("generic", os.ModePerm) + if err != nil { + return err + } + fg, err := os.Create(filepath.Clean(genFilename)) + if err != nil { + return err + } + defer fg.Close() + + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + G "github.com/IBM/fp-go/v2/%s/generic" +) +`, pkg) + + // some header + fmt.Fprintln(fg, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(fg, "// This file was generated by robots at") + fmt.Fprintf(fg, "// %s\n", time.Now()) + + fmt.Fprintf(fg, "package generic\n\n") + + fmt.Fprintf(fg, ` +import ( + E "github.com/IBM/fp-go/v2/either" + RD "github.com/IBM/fp-go/v2/reader/generic" +) +`) + + // from + generateReaderIOEitherFrom(f, fg, 0) + // eitherize + generateReaderIOEitherEitherize(f, fg, 0) + // uneitherize + generateReaderIOEitherUneitherize(f, fg, 0) + + for i := 1; i <= count; i++ { + // from + generateReaderIOEitherFrom(f, fg, i) + // eitherize + generateReaderIOEitherEitherize(f, fg, i) + // uneitherize + generateReaderIOEitherUneitherize(f, fg, i) + } + + return nil +} + +func ReaderIOEitherCommand() *C.Command { + return &C.Command{ + Name: "readerioeither", + Usage: "generate code for ReaderIOEither", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateReaderIOEitherHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/cli/templates/functions.go b/v2/cli/templates/functions.go new file mode 100644 index 0000000..2ed4f6f --- /dev/null +++ b/v2/cli/templates/functions.go @@ -0,0 +1,15 @@ +package templates + +import ( + "text/template" + + E "github.com/IBM/fp-go/v2/either" +) + +var ( + templateFunctions = template.FuncMap{} +) + +func Parse(name, tmpl string) E.Either[error, *template.Template] { + return E.TryCatchError(template.New(name).Funcs(templateFunctions).Parse(tmpl)) +} diff --git a/v2/cli/tuple.go b/v2/cli/tuple.go new file mode 100644 index 0000000..ca32a4b --- /dev/null +++ b/v2/cli/tuple.go @@ -0,0 +1,625 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + "time" + + C "github.com/urfave/cli/v2" +) + +func writeTupleType(f *os.File, symbol string, i int) { + fmt.Fprintf(f, "Tuple%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "%s%d", symbol, j) + } + fmt.Fprintf(f, "]") +} + +func makeTupleType(name string) func(i int) string { + return func(i int) string { + var buf strings.Builder + buf.WriteString(fmt.Sprintf("Tuple%d[", i)) + for j := 0; j < i; j++ { + if j > 0 { + buf.WriteString(", ") + } + buf.WriteString(fmt.Sprintf("%s%d", name, j+1)) + } + buf.WriteString("]") + + return buf.String() + } +} + +func generatePush(f *os.File, i int) { + tuple1 := makeTupleType("T")(i) + tuple2 := makeTupleType("T")(i + 1) + // Create the replicate version + fmt.Fprintf(f, "\n// Push%d creates a [Tuple%d] from a [Tuple%d] by appending a constant value\n", i, i+1, i) + fmt.Fprintf(f, "func Push%d[", i) + // function prototypes + for j := 0; j <= i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, " any](value T%d) func(%s) %s {\n", i+1, tuple1, tuple2) + fmt.Fprintf(f, " return func(t %s) %s {\n", tuple1, tuple2) + fmt.Fprintf(f, " return MakeTuple%d(", i+1) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t.F%d", j+1) + } + fmt.Fprintf(f, ", value)\n") + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, "}\n") +} + +func generateReplicate(f *os.File, i int) { + // Create the replicate version + fmt.Fprintf(f, "\n// Replicate%d creates a [Tuple%d] with all fields set to the input value `t`\n", i, i) + fmt.Fprintf(f, "func Replicate%d[T any](t T) Tuple%d[", i, i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T") + } + fmt.Fprintf(f, "] {\n") + // execute the mapping + fmt.Fprintf(f, " return MakeTuple%d(", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t") + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") +} + +func generateMap(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Map%d maps each value of a [Tuple%d] via a mapping function\n", i, i) + fmt.Fprintf(f, "func Map%d[", i) + // function prototypes + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "F%d ~func(T%d) R%d", j, j, j) + } + for j := 1; j <= i; j++ { + fmt.Fprintf(f, ", T%d, R%d", j, j) + } + fmt.Fprintf(f, " any](") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d F%d", j, j) + } + fmt.Fprintf(f, ") func(") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") ") + writeTupleType(f, "R", i) + fmt.Fprintf(f, " {\n") + + fmt.Fprintf(f, " return func(t ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") ") + writeTupleType(f, "R", i) + fmt.Fprintf(f, " {\n") + + // execute the mapping + fmt.Fprintf(f, " return MakeTuple%d(\n", i) + for j := 1; j <= i; j++ { + fmt.Fprintf(f, " f%d(t.F%d),\n", j, j) + } + fmt.Fprintf(f, " )\n") + + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, "}\n") +} + +func generateMonoid(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Monoid%d creates a [Monoid] for a [Tuple%d] based on %d monoids for the contained types\n", i, i, i) + fmt.Fprintf(f, "func Monoid%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, " any](") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "m%d M.Monoid[T%d]", j, j) + } + fmt.Fprintf(f, ") M.Monoid[") + writeTupleType(f, "T", i) + fmt.Fprintf(f, "] {\n") + + fmt.Fprintf(f, " return M.MakeMonoid(func(l, r ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, "{\n") + + fmt.Fprintf(f, " return MakeTuple%d(", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "m%d.Concat(l.F%d, r.F%d)", j, j, j) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, " }, MakeTuple%d(", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "m%d.Empty()", j) + } + fmt.Fprintf(f, "))\n") + + fmt.Fprintf(f, "}\n") +} + +func generateOrd(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Ord%d creates n [Ord] for a [Tuple%d] based on %d [Ord]s for the contained types\n", i, i, i) + fmt.Fprintf(f, "func Ord%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, " any](") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "o%d O.Ord[T%d]", j, j) + } + fmt.Fprintf(f, ") O.Ord[") + writeTupleType(f, "T", i) + fmt.Fprintf(f, "] {\n") + + fmt.Fprintf(f, " return O.MakeOrd(func(l, r ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") int {\n") + + for j := 1; j <= i; j++ { + fmt.Fprintf(f, " if c:= o%d.Compare(l.F%d, r.F%d); c != 0 {return c}\n", j, j, j) + } + fmt.Fprintf(f, " return 0\n") + fmt.Fprintf(f, " }, func(l, r ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") bool {\n") + fmt.Fprintf(f, " return ") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, " && ") + } + fmt.Fprintf(f, "o%d.Equals(l.F%d, r.F%d)", j, j, j) + } + fmt.Fprintf(f, "\n") + fmt.Fprintf(f, " })\n") + + fmt.Fprintf(f, "}\n") +} + +func generateTupleType(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Tuple%d is a struct that carries %d independently typed values\n", i, i) + fmt.Fprintf(f, "type Tuple%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, " any] struct {\n") + for j := 1; j <= i; j++ { + fmt.Fprintf(f, " F%d T%d\n", j, j) + } + fmt.Fprintf(f, "}\n") +} + +func generateMakeTupleType(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// MakeTuple%d is a function that converts its %d parameters into a [Tuple%d]\n", i, i, i) + fmt.Fprintf(f, "func MakeTuple%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, " any](") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j, j) + } + fmt.Fprintf(f, ") ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, " {\n") + fmt.Fprintf(f, " return Tuple%d[", i) + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j) + } + fmt.Fprintf(f, "]{") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j) + } + fmt.Fprintf(f, "}\n") + fmt.Fprintf(f, "}\n") +} + +func generateUntupled(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Untupled%d converts a function with a [Tuple%d] parameter into a function with %d parameters\n// The inverse function is [Tupled%d]\n", i, i, i, i) + fmt.Fprintf(f, "func Untupled%d[F ~func(Tuple%d[", i, i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "]) R") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j+1) + } + fmt.Fprintf(f, ", R any](f F) func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") R {\n") + fmt.Fprintf(f, " return func(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d T%d", j+1, j+1) + } + fmt.Fprintf(f, ") R {\n") + fmt.Fprintf(f, " return f(MakeTuple%d(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t%d", j+1) + } + fmt.Fprintln(f, "))") + fmt.Fprintln(f, " }") + fmt.Fprintln(f, "}") +} + +func generateTupled(f *os.File, i int) { + // Create the optionize version + fmt.Fprintf(f, "\n// Tupled%d converts a function with %d parameters into a function taking a Tuple%d\n// The inverse function is [Untupled%d]\n", i, i, i, i) + fmt.Fprintf(f, "func Tupled%d[F ~func(", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, ") R") + for j := 0; j < i; j++ { + fmt.Fprintf(f, ", T%d", j+1) + } + fmt.Fprintf(f, ", R any](f F) func(Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "]) R {\n") + fmt.Fprintf(f, " return func(t Tuple%d[", i) + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "T%d", j+1) + } + fmt.Fprintf(f, "]) R {\n") + fmt.Fprintf(f, " return f(") + for j := 0; j < i; j++ { + if j > 0 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t.F%d", j+1) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, " }\n") + fmt.Fprintln(f, "}") +} + +func generateTupleHelpers(filename string, count int) error { + dir, err := os.Getwd() + if err != nil { + return err + } + absDir, err := filepath.Abs(dir) + if err != nil { + return err + } + pkg := filepath.Base(absDir) + f, err := os.Create(filepath.Clean(filename)) + if err != nil { + return err + } + defer f.Close() + // log + log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) + + // some header + fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") + fmt.Fprintln(f, "// This file was generated by robots at") + fmt.Fprintf(f, "// %s\n\n", time.Now()) + + fmt.Fprintf(f, "package %s\n\n", pkg) + + fmt.Fprintf(f, ` +import ( + M "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/ord" +) +`) + + for i := 1; i <= count; i++ { + // tuple type + generateTupleType(f, i) + } + + for i := 1; i <= count; i++ { + // tuple generator + generateMakeTupleType(f, i) + // tupled wrapper + generateTupled(f, i) + // untupled wrapper + generateUntupled(f, i) + // monoid + generateMonoid(f, i) + // generate order + generateOrd(f, i) + // generate map + generateMap(f, i) + // generate replicate + generateReplicate(f, i) + // generate tuple functions such as string and fmt + generateTupleString(f, i) + // generate json support + generateTupleMarshal(f, i) + // generate json support + generateTupleUnmarshal(f, i) + // generate toArray + generateToArray(f, i) + // generate fromArray + generateFromArray(f, i) + // generate push + if i < count { + generatePush(f, i) + } + } + + return nil +} + +func generateTupleMarshal(f *os.File, i int) { + // Create the stringify version + fmt.Fprintf(f, "\n// MarshalJSON marshals the [Tuple%d] into a JSON array\n", i) + fmt.Fprintf(f, "func (t ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n") + fmt.Fprintf(f, " return tupleMarshalJSON(") + // function prototypes + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t.F%d", j) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") +} + +func generateTupleUnmarshal(f *os.File, i int) { + // Create the stringify version + fmt.Fprintf(f, "\n// UnmarshalJSON unmarshals a JSON array into a [Tuple%d]\n", i) + fmt.Fprintf(f, "func (t *") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") UnmarshalJSON(data []byte) error {\n") + fmt.Fprintf(f, " return tupleUnmarshalJSON(data") + // function prototypes + for j := 1; j <= i; j++ { + fmt.Fprintf(f, ", &t.F%d", j) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") +} + +func generateToArray(f *os.File, i int) { + // Create the stringify version + fmt.Fprintf(f, "\n// ToArray converts the [Tuple%d] into an array of type [R] using %d transformation functions from [T] to [R]\n// The inverse function is [FromArray%d]\n", i, i, i) + fmt.Fprintf(f, "func ToArray%d[", i) + // function prototypes + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "F%d ~func(T%d) R", j, j) + } + for j := 1; j <= i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", R any](") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d F%d", j, j) + } + fmt.Fprintf(f, ") func(t ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") []R {\n") + fmt.Fprintf(f, " return func(t ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") []R {\n") + fmt.Fprintf(f, " return []R{\n") + for j := 1; j <= i; j++ { + fmt.Fprintf(f, " f%d(t.F%d),\n", j, j) + } + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, "}\n") +} + +func generateFromArray(f *os.File, i int) { + // Create the stringify version + fmt.Fprintf(f, "\n// FromArray converts an array of [R] into a [Tuple%d] using %d functions from [R] to [T]\n// The inverse function is [ToArray%d]\n", i, i, i) + fmt.Fprintf(f, "func FromArray%d[", i) + // function prototypes + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "F%d ~func(R) T%d", j, j) + } + for j := 1; j <= i; j++ { + fmt.Fprintf(f, ", T%d", j) + } + fmt.Fprintf(f, ", R any](") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "f%d F%d", j, j) + } + fmt.Fprintf(f, ") func(r []R) ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, " {\n") + fmt.Fprintf(f, " return func(r []R) ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, " {\n") + fmt.Fprintf(f, " return MakeTuple%d(\n", i) + for j := 1; j <= i; j++ { + fmt.Fprintf(f, " f%d(r[%d]),\n", j, j-1) + } + fmt.Fprintf(f, " )\n") + fmt.Fprintf(f, " }\n") + fmt.Fprintf(f, "}\n") +} + +func generateTupleString(f *os.File, i int) { + // Create the stringify version + fmt.Fprintf(f, "\n// String prints some debug info for the [Tuple%d]\n", i) + fmt.Fprintf(f, "func (t ") + writeTupleType(f, "T", i) + fmt.Fprintf(f, ") String() string {\n") + // convert to string + fmt.Fprint(f, " return tupleString(") + for j := 1; j <= i; j++ { + if j > 1 { + fmt.Fprintf(f, ", ") + } + fmt.Fprintf(f, "t.F%d", j) + } + fmt.Fprintf(f, ")\n") + fmt.Fprintf(f, "}\n") +} + +// func generateTupleJson(f *os.File, i int) { +// // Create the stringify version +// fmt.Fprintf(f, "\n// MarshalJSON converts the [Tuple%d] into a JSON byte stream\n", i) +// fmt.Fprintf(f, "func (t ") +// writeTupleType(f, "T", i) +// fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n") +// // convert to string +// fmt.Fprintf(f, " return fmt.Sprintf(\"Tuple%d[", i) +// for j := 1; j <= i; j++ { +// if j > 1 { +// fmt.Fprintf(f, ", ") +// } +// fmt.Fprintf(f, "%s", "%T") +// } +// fmt.Fprintf(f, "](") +// for j := 1; j <= i; j++ { +// if j > 1 { +// fmt.Fprintf(f, ", ") +// } +// fmt.Fprintf(f, "%s", "%v") +// } +// fmt.Fprintf(f, ")\", ") +// for j := 1; j <= i; j++ { +// if j > 1 { +// fmt.Fprintf(f, ", ") +// } +// fmt.Fprintf(f, "t.F%d", j) +// } +// for j := 1; j <= i; j++ { +// fmt.Fprintf(f, ", t.F%d", j) +// } +// fmt.Fprintf(f, ")\n") +// fmt.Fprintf(f, "}\n") +// } + +func TupleCommand() *C.Command { + return &C.Command{ + Name: "tuple", + Usage: "generate code for Tuple", + Flags: []C.Flag{ + flagCount, + flagFilename, + }, + Action: func(ctx *C.Context) error { + return generateTupleHelpers( + ctx.String(keyFilename), + ctx.Int(keyCount), + ) + }, + } +} diff --git a/v2/constant/const.go b/v2/constant/const.go new file mode 100644 index 0000000..eb87384 --- /dev/null +++ b/v2/constant/const.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package constant + +import ( + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +type Const[E, A any] struct { + value E +} + +func Make[E, A any](e E) Const[E, A] { + return Const[E, A]{value: e} +} + +func Unwrap[E, A any](c Const[E, A]) E { + return c.value +} + +func Of[E, A any](m M.Monoid[E]) func(A) Const[E, A] { + return F.Constant1[A](Make[E, A](m.Empty())) +} + +func MonadMap[E, A, B any](fa Const[E, A], _ func(A) B) Const[E, B] { + return Make[E, B](fa.value) +} + +func MonadAp[E, A, B any](s S.Semigroup[E]) func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] { + return func(fab Const[E, func(A) B], fa Const[E, A]) Const[E, B] { + return Make[E, B](s.Concat(fab.value, fa.value)) + } +} + +func Map[E, A, B any](f func(A) B) func(fa Const[E, A]) Const[E, B] { + return F.Bind2nd(MonadMap[E, A, B], f) +} + +func Ap[E, A, B any](s S.Semigroup[E]) func(fa Const[E, A]) func(fab Const[E, func(A) B]) Const[E, B] { + monadap := MonadAp[E, A, B](s) + return func(fa Const[E, A]) func(fab Const[E, func(A) B]) Const[E, B] { + return F.Bind2nd(monadap, fa) + } +} diff --git a/v2/constant/const_test.go b/v2/constant/const_test.go new file mode 100644 index 0000000..a1f6d8a --- /dev/null +++ b/v2/constant/const_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package constant + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + S "github.com/IBM/fp-go/v2/string" + + "github.com/stretchr/testify/assert" +) + +func TestMap(t *testing.T) { + fa := Make[string, int]("foo") + assert.Equal(t, fa, F.Pipe1(fa, Map[string, int](utils.Double))) +} + +func TestOf(t *testing.T) { + assert.Equal(t, Make[string, int](""), Of[string, int](S.Monoid)(1)) +} + +func TestAp(t *testing.T) { + fab := Make[string, int]("bar") + assert.Equal(t, Make[string, int]("foobar"), Ap[string, int, int](S.Monoid)(fab)(Make[string, func(int) int]("foo"))) +} diff --git a/v2/constraints/constraints.go b/v2/constraints/constraints.go new file mode 100644 index 0000000..73fe36d --- /dev/null +++ b/v2/constraints/constraints.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package constraints + +type Ordered interface { + Integer | Float | ~string +} + +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +type Integer interface { + Signed | Unsigned +} + +type Float interface { + ~float32 | ~float64 +} + +type Complex interface { + ~complex64 | ~complex128 +} diff --git a/v2/context/doc.go b/v2/context/doc.go new file mode 100644 index 0000000..89597b2 --- /dev/null +++ b/v2/context/doc.go @@ -0,0 +1,17 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package context contains versions of reader IO monads that work with a golang [context.Context] +package context diff --git a/v2/context/ioeither/generic/ioeither.go b/v2/context/ioeither/generic/ioeither.go new file mode 100644 index 0000000..799f393 --- /dev/null +++ b/v2/context/ioeither/generic/ioeither.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + IOE "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// WithContext wraps an existing IOEither and performs a context check for cancellation before delegating +func WithContext[GIO ~func() E.Either[error, A], A any](ctx context.Context, ma GIO) GIO { + return IOE.MakeIO[GIO](func() E.Either[error, A] { + if err := context.Cause(ctx); err != nil { + return E.Left[A](err) + } + return ma() + }) +} diff --git a/v2/context/ioeither/ioeither.go b/v2/context/ioeither/ioeither.go new file mode 100644 index 0000000..2d312d6 --- /dev/null +++ b/v2/context/ioeither/ioeither.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "context" + + G "github.com/IBM/fp-go/v2/context/ioeither/generic" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +// withContext wraps an existing IOEither and performs a context check for cancellation before delegating +func WithContext[A any](ctx context.Context, ma IOE.IOEither[error, A]) IOE.IOEither[error, A] { + return G.WithContext(ctx, ma) +} diff --git a/v2/context/readereither/array.go b/v2/context/readereither/array.go new file mode 100644 index 0000000..6227b28 --- /dev/null +++ b/v2/context/readereither/array.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + RE "github.com/IBM/fp-go/v2/readereither/generic" +) + +// TraverseArray transforms an array +func TraverseArray[A, B any](f func(A) ReaderEither[B]) func([]A) ReaderEither[[]B] { + return RE.TraverseArray[ReaderEither[B], ReaderEither[[]B], []A](f) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) func([]A) ReaderEither[[]B] { + return RE.TraverseArrayWithIndex[ReaderEither[B], ReaderEither[[]B], []A](f) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[A any](ma []ReaderEither[A]) ReaderEither[[]A] { + return RE.SequenceArray[ReaderEither[A], ReaderEither[[]A]](ma) +} diff --git a/v2/context/readereither/bind.go b/v2/context/readereither/bind.go new file mode 100644 index 0000000..9e23969 --- /dev/null +++ b/v2/context/readereither/bind.go @@ -0,0 +1,68 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "context" + + G "github.com/IBM/fp-go/v2/readereither/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) ReaderEither[S] { + return G.Do[ReaderEither[S], context.Context, error, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) ReaderEither[T], +) func(ReaderEither[S1]) ReaderEither[S2] { + return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(ReaderEither[S1]) ReaderEither[S2] { + return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(ReaderEither[S1]) ReaderEither[S2] { + return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func(ReaderEither[T]) ReaderEither[S1] { + return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa ReaderEither[T], +) func(ReaderEither[S1]) ReaderEither[S2] { + return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa) +} diff --git a/v2/context/readereither/bind_test.go b/v2/context/readereither/bind_test.go new file mode 100644 index 0000000..3320e7c --- /dev/null +++ b/v2/context/readereither/bind_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "context" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) ReaderEither[string] { + return Of("Doe") +} + +func getGivenName(s utils.WithLastName) ReaderEither[string] { + return Of("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(context.Background()), E.Of[error]("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + ApS(utils.SetLastName, Of("Doe")), + ApS(utils.SetGivenName, Of("John")), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(context.Background()), E.Of[error]("John Doe")) +} diff --git a/v2/context/readereither/cancel.go b/v2/context/readereither/cancel.go new file mode 100644 index 0000000..76e9790 --- /dev/null +++ b/v2/context/readereither/cancel.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" +) + +// withContext wraps an existing ReaderEither and performs a context check for cancellation before deletating +func WithContext[A any](ma ReaderEither[A]) ReaderEither[A] { + return func(ctx context.Context) E.Either[error, A] { + if err := context.Cause(ctx); err != nil { + return E.Left[A](err) + } + return ma(ctx) + } +} diff --git a/v2/context/readereither/curry.go b/v2/context/readereither/curry.go new file mode 100644 index 0000000..200e749 --- /dev/null +++ b/v2/context/readereither/curry.go @@ -0,0 +1,53 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "context" + + RE "github.com/IBM/fp-go/v2/readereither/generic" +) + +// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func Curry0[A any](f func(context.Context) (A, error)) ReaderEither[A] { + return RE.Curry0[ReaderEither[A]](f) +} + +func Curry1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] { + return RE.Curry1[ReaderEither[A]](f) +} + +func Curry2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1) func(T2) ReaderEither[A] { + return RE.Curry2[ReaderEither[A]](f) +} + +func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) ReaderEither[A] { + return RE.Curry3[ReaderEither[A]](f) +} + +func Uncurry1[T1, A any](f func(T1) ReaderEither[A]) func(context.Context, T1) (A, error) { + return RE.Uncurry1(f) +} + +func Uncurry2[T1, T2, A any](f func(T1) func(T2) ReaderEither[A]) func(context.Context, T1, T2) (A, error) { + return RE.Uncurry2(f) +} + +func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderEither[A]) func(context.Context, T1, T2, T3) (A, error) { + return RE.Uncurry3(f) +} diff --git a/v2/context/readereither/exec/exec.go b/v2/context/readereither/exec/exec.go new file mode 100644 index 0000000..6c74302 --- /dev/null +++ b/v2/context/readereither/exec/exec.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package exec + +import ( + "context" + + RE "github.com/IBM/fp-go/v2/context/readereither" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/exec" + F "github.com/IBM/fp-go/v2/function" + GE "github.com/IBM/fp-go/v2/internal/exec" +) + +var ( + // Command executes a command + // use this version if the command does not produce any side effect, i.e. if the output is uniquely determined by by the input + // typically you'd rather use the ReaderIOEither version of the command + Command = F.Curry3(command) +) + +func command(name string, args []string, in []byte) RE.ReaderEither[exec.CommandOutput] { + return func(ctx context.Context) E.Either[error, exec.CommandOutput] { + return E.TryCatchError(GE.Exec(ctx, name, args, in)) + } +} diff --git a/v2/context/readereither/from.go b/v2/context/readereither/from.go new file mode 100644 index 0000000..d606a4f --- /dev/null +++ b/v2/context/readereither/from.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "context" + + RE "github.com/IBM/fp-go/v2/readereither/generic" +) + +// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func From0[A any](f func(context.Context) (A, error)) func() ReaderEither[A] { + return RE.From0[ReaderEither[A]](f) +} + +func From1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] { + return RE.From1[ReaderEither[A]](f) +} + +func From2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1, T2) ReaderEither[A] { + return RE.From2[ReaderEither[A]](f) +} + +func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderEither[A] { + return RE.From3[ReaderEither[A]](f) +} diff --git a/v2/context/readereither/reader.go b/v2/context/readereither/reader.go new file mode 100644 index 0000000..1dad92a --- /dev/null +++ b/v2/context/readereither/reader.go @@ -0,0 +1,100 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "context" + + ET "github.com/IBM/fp-go/v2/either" + O "github.com/IBM/fp-go/v2/option" + RE "github.com/IBM/fp-go/v2/readereither/generic" +) + +func MakeReaderEither[A any](f func(context.Context) ET.Either[error, A]) ReaderEither[A] { + return RE.MakeReaderEither[ReaderEither[A]](f) +} + +func FromEither[A any](e ET.Either[error, A]) ReaderEither[A] { + return RE.FromEither[ReaderEither[A]](e) +} + +func Left[A any](l error) ReaderEither[A] { + return RE.Left[ReaderEither[A]](l) +} + +func Right[A any](r A) ReaderEither[A] { + return RE.Right[ReaderEither[A]](r) +} + +func MonadMap[A, B any](fa ReaderEither[A], f func(A) B) ReaderEither[B] { + return RE.MonadMap[ReaderEither[A], ReaderEither[B]](fa, f) +} + +func Map[A, B any](f func(A) B) func(ReaderEither[A]) ReaderEither[B] { + return RE.Map[ReaderEither[A], ReaderEither[B]](f) +} + +func MonadChain[A, B any](ma ReaderEither[A], f func(A) ReaderEither[B]) ReaderEither[B] { + return RE.MonadChain(ma, f) +} + +func Chain[A, B any](f func(A) ReaderEither[B]) func(ReaderEither[A]) ReaderEither[B] { + return RE.Chain[ReaderEither[A]](f) +} + +func Of[A any](a A) ReaderEither[A] { + return RE.Of[ReaderEither[A]](a) +} + +func MonadAp[A, B any](fab ReaderEither[func(A) B], fa ReaderEither[A]) ReaderEither[B] { + return RE.MonadAp[ReaderEither[A], ReaderEither[B]](fab, fa) +} + +func Ap[A, B any](fa ReaderEither[A]) func(ReaderEither[func(A) B]) ReaderEither[B] { + return RE.Ap[ReaderEither[A], ReaderEither[B], ReaderEither[func(A) B]](fa) +} + +func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) ReaderEither[A] { + return RE.FromPredicate[ReaderEither[A]](pred, onFalse) +} + +func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] { + return RE.OrElse[ReaderEither[A]](onLeft) +} + +func Ask() ReaderEither[context.Context] { + return RE.Ask[ReaderEither[context.Context]]() +} + +func MonadChainEitherK[A, B any](ma ReaderEither[A], f func(A) ET.Either[error, B]) ReaderEither[B] { + return RE.MonadChainEitherK[ReaderEither[A], ReaderEither[B]](ma, f) +} + +func ChainEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderEither[A]) ReaderEither[B] { + return RE.ChainEitherK[ReaderEither[A], ReaderEither[B]](f) +} + +func ChainOptionK[A, B any](onNone func() error) func(func(A) O.Option[B]) func(ReaderEither[A]) ReaderEither[B] { + return RE.ChainOptionK[ReaderEither[A], ReaderEither[B]](onNone) +} + +func MonadFlap[B, A any](fab ReaderEither[func(A) B], a A) ReaderEither[B] { + return RE.MonadFlap[ReaderEither[func(A) B], ReaderEither[B]](fab, a) +} + +func Flap[B, A any](a A) func(ReaderEither[func(A) B]) ReaderEither[B] { + return RE.Flap[ReaderEither[func(A) B], ReaderEither[B]](a) +} diff --git a/v2/context/readereither/sequence.go b/v2/context/readereither/sequence.go new file mode 100644 index 0000000..1470791 --- /dev/null +++ b/v2/context/readereither/sequence.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + RE "github.com/IBM/fp-go/v2/readereither/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[A any](a ReaderEither[A]) ReaderEither[T.Tuple1[A]] { + return RE.SequenceT1[ + ReaderEither[A], + ReaderEither[T.Tuple1[A]], + ](a) +} + +func SequenceT2[A, B any](a ReaderEither[A], b ReaderEither[B]) ReaderEither[T.Tuple2[A, B]] { + return RE.SequenceT2[ + ReaderEither[A], + ReaderEither[B], + ReaderEither[T.Tuple2[A, B]], + ](a, b) +} + +func SequenceT3[A, B, C any](a ReaderEither[A], b ReaderEither[B], c ReaderEither[C]) ReaderEither[T.Tuple3[A, B, C]] { + return RE.SequenceT3[ + ReaderEither[A], + ReaderEither[B], + ReaderEither[C], + ReaderEither[T.Tuple3[A, B, C]], + ](a, b, c) +} + +func SequenceT4[A, B, C, D any](a ReaderEither[A], b ReaderEither[B], c ReaderEither[C], d ReaderEither[D]) ReaderEither[T.Tuple4[A, B, C, D]] { + return RE.SequenceT4[ + ReaderEither[A], + ReaderEither[B], + ReaderEither[C], + ReaderEither[D], + ReaderEither[T.Tuple4[A, B, C, D]], + ](a, b, c, d) +} diff --git a/v2/context/readereither/type.go b/v2/context/readereither/type.go new file mode 100644 index 0000000..25b30ff --- /dev/null +++ b/v2/context/readereither/type.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package readereither implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error +package readereither + +import ( + "context" + + RE "github.com/IBM/fp-go/v2/readereither" +) + +// ReaderEither is a specialization of the Reader monad for the typical golang scenario +type ReaderEither[A any] RE.ReaderEither[context.Context, error, A] diff --git a/v2/context/readerioeither/bind.go b/v2/context/readerioeither/bind.go new file mode 100644 index 0000000..ef53ab2 --- /dev/null +++ b/v2/context/readerioeither/bind.go @@ -0,0 +1,67 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) ReaderIOEither[S] { + return G.Do[ReaderIOEither[S], IOE.IOEither[error, S], S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) ReaderIOEither[T], +) func(ReaderIOEither[S1]) ReaderIOEither[S2] { + return G.Bind[ReaderIOEither[S1], ReaderIOEither[S2], ReaderIOEither[T], IOE.IOEither[error, S1], IOE.IOEither[error, S2], IOE.IOEither[error, T], S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(ReaderIOEither[S1]) ReaderIOEither[S2] { + return G.Let[ReaderIOEither[S1], ReaderIOEither[S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(ReaderIOEither[S1]) ReaderIOEither[S2] { + return G.LetTo[ReaderIOEither[S1], ReaderIOEither[S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func(ReaderIOEither[T]) ReaderIOEither[S1] { + return G.BindTo[ReaderIOEither[S1], ReaderIOEither[T], IOE.IOEither[error, S1], IOE.IOEither[error, T], S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa ReaderIOEither[T], +) func(ReaderIOEither[S1]) ReaderIOEither[S2] { + return G.ApS[ReaderIOEither[func(T) S2], ReaderIOEither[S1], ReaderIOEither[S2], ReaderIOEither[T], IOE.IOEither[error, func(T) S2], IOE.IOEither[error, S1], IOE.IOEither[error, S2], IOE.IOEither[error, T], S1, S2, T](setter, fa) +} diff --git a/v2/context/readerioeither/bind_test.go b/v2/context/readerioeither/bind_test.go new file mode 100644 index 0000000..b640a24 --- /dev/null +++ b/v2/context/readerioeither/bind_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) ReaderIOEither[string] { + return Of("Doe") +} + +func getGivenName(s utils.WithLastName) ReaderIOEither[string] { + return Of("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + ApS(utils.SetLastName, Of("Doe")), + ApS(utils.SetGivenName, Of("John")), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe")) +} diff --git a/v2/context/readerioeither/bracket.go b/v2/context/readerioeither/bracket.go new file mode 100644 index 0000000..4e92644 --- /dev/null +++ b/v2/context/readerioeither/bracket.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" + E "github.com/IBM/fp-go/v2/either" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[ + A, B, ANY any]( + + acquire ReaderIOEither[A], + use func(A) ReaderIOEither[B], + release func(A, E.Either[error, B]) ReaderIOEither[ANY], +) ReaderIOEither[B] { + return G.Bracket[ReaderIOEither[A], ReaderIOEither[B], ReaderIOEither[ANY]]( + acquire, + use, + release, + ) +} diff --git a/v2/context/readerioeither/cancel.go b/v2/context/readerioeither/cancel.go new file mode 100644 index 0000000..e1af8b4 --- /dev/null +++ b/v2/context/readerioeither/cancel.go @@ -0,0 +1,25 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" +) + +// WithContext wraps an existing [ReaderIOEither] and performs a context check for cancellation before delegating +func WithContext[A any](ma ReaderIOEither[A]) ReaderIOEither[A] { + return G.WithContext(ma) +} diff --git a/v2/context/readerioeither/data/file.txt b/v2/context/readerioeither/data/file.txt new file mode 100644 index 0000000..f6a7bf5 --- /dev/null +++ b/v2/context/readerioeither/data/file.txt @@ -0,0 +1 @@ +Carsten \ No newline at end of file diff --git a/v2/context/readerioeither/doc.go b/v2/context/readerioeither/doc.go new file mode 100644 index 0000000..25e752f --- /dev/null +++ b/v2/context/readerioeither/doc.go @@ -0,0 +1,19 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package readerioeither contains a version of [ReaderIOEither] that takes a golang [context.Context] as its context and that assumes the standard go error +package readerioeither + +//go:generate go run ../.. contextreaderioeither --count 10 --filename gen.go diff --git a/v2/context/readerioeither/eq.go b/v2/context/readerioeither/eq.go new file mode 100644 index 0000000..da9625d --- /dev/null +++ b/v2/context/readerioeither/eq.go @@ -0,0 +1,29 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" +) + +// Eq implements the equals predicate for values contained in the [ReaderIOEither] monad +func Eq[A any](eq EQ.Eq[ET.Either[error, A]]) func(context.Context) EQ.Eq[ReaderIOEither[A]] { + return G.Eq[ReaderIOEither[A]](eq) +} diff --git a/v2/context/readerioeither/exec/exec.go b/v2/context/readerioeither/exec/exec.go new file mode 100644 index 0000000..47b2729 --- /dev/null +++ b/v2/context/readerioeither/exec/exec.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package exec + +import ( + "context" + + RIOE "github.com/IBM/fp-go/v2/context/readerioeither" + "github.com/IBM/fp-go/v2/exec" + F "github.com/IBM/fp-go/v2/function" + GE "github.com/IBM/fp-go/v2/internal/exec" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +var ( + // Command executes a cancelable command + Command = F.Curry3(command) +) + +func command(name string, args []string, in []byte) RIOE.ReaderIOEither[exec.CommandOutput] { + return func(ctx context.Context) IOE.IOEither[error, exec.CommandOutput] { + return IOE.TryCatchError(func() (exec.CommandOutput, error) { + return GE.Exec(ctx, name, args, in) + }) + } +} diff --git a/v2/context/readerioeither/file/data/file.json b/v2/context/readerioeither/file/data/file.json new file mode 100644 index 0000000..6f6b333 --- /dev/null +++ b/v2/context/readerioeither/file/data/file.json @@ -0,0 +1,3 @@ +{ + "data": "Carsten" +} \ No newline at end of file diff --git a/v2/context/readerioeither/file/file.go b/v2/context/readerioeither/file/file.go new file mode 100644 index 0000000..6713ca2 --- /dev/null +++ b/v2/context/readerioeither/file/file.go @@ -0,0 +1,64 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "context" + "io" + "os" + + RIOE "github.com/IBM/fp-go/v2/context/readerioeither" + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/file" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOEF "github.com/IBM/fp-go/v2/ioeither/file" +) + +var ( + // Open opens a file for reading within the given context + Open = F.Flow3( + IOEF.Open, + RIOE.FromIOEither[*os.File], + RIOE.WithContext[*os.File], + ) + + // Remove removes a file by name + Remove = F.Flow2( + IOEF.Remove, + RIOE.FromIOEither[string], + ) +) + +// Close closes an object +func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] { + return F.Pipe2( + c, + IOEF.Close[C], + RIOE.FromIOEither[any], + ) +} + +// ReadFile reads a file in the scope of a context +func ReadFile(path string) RIOE.ReaderIOEither[[]byte] { + return RIOE.WithResource[[]byte](Open(path), Close[*os.File])(func(r *os.File) RIOE.ReaderIOEither[[]byte] { + return func(ctx context.Context) IOE.IOEither[error, []byte] { + return IOE.MakeIO(func() ET.Either[error, []byte] { + return file.ReadAll(ctx, r) + }) + } + }) +} diff --git a/v2/context/readerioeither/file/file_test.go b/v2/context/readerioeither/file/file_test.go new file mode 100644 index 0000000..68da4bc --- /dev/null +++ b/v2/context/readerioeither/file/file_test.go @@ -0,0 +1,51 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "context" + "fmt" + + R "github.com/IBM/fp-go/v2/context/readerioeither" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" + J "github.com/IBM/fp-go/v2/json" +) + +type RecordType struct { + Data string `json:"data"` +} + +func getData(r RecordType) string { + return r.Data +} + +func ExampleReadFile() { + + data := F.Pipe3( + ReadFile("./data/file.json"), + R.ChainEitherK(J.Unmarshal[RecordType]), + R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")), + R.Map(getData), + ) + + result := data(context.Background()) + + fmt.Println(result()) + + // Output: + // Right[string](Carsten) +} diff --git a/v2/context/readerioeither/file/tempfile.go b/v2/context/readerioeither/file/tempfile.go new file mode 100644 index 0000000..6023092 --- /dev/null +++ b/v2/context/readerioeither/file/tempfile.go @@ -0,0 +1,52 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "os" + + RIOE "github.com/IBM/fp-go/v2/context/readerioeither" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" + IOF "github.com/IBM/fp-go/v2/io/file" + IOEF "github.com/IBM/fp-go/v2/ioeither/file" +) + +var ( + // onCreateTempFile creates a temp file with sensible defaults + onCreateTempFile = CreateTemp("", "*") + // destroy handler + onReleaseTempFile = F.Flow4( + IOF.Close[*os.File], + IO.Map((*os.File).Name), + RIOE.FromIO[string], + RIOE.Chain(Remove), + ) +) + +// CreateTemp created a temp file with proper parametrization +func CreateTemp(dir, pattern string) RIOE.ReaderIOEither[*os.File] { + return F.Pipe2( + IOEF.CreateTemp(dir, pattern), + RIOE.FromIOEither[*os.File], + RIOE.WithContext[*os.File], + ) +} + +// WithTempFile creates a temporary file, then invokes a callback to create a resource based on the file, then close and remove the temp file +func WithTempFile[A any](f func(*os.File) RIOE.ReaderIOEither[A]) RIOE.ReaderIOEither[A] { + return RIOE.WithResource[A](onCreateTempFile, onReleaseTempFile)(f) +} diff --git a/v2/context/readerioeither/file/tempfile_test.go b/v2/context/readerioeither/file/tempfile_test.go new file mode 100644 index 0000000..4586452 --- /dev/null +++ b/v2/context/readerioeither/file/tempfile_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "context" + "os" + "testing" + + RIOE "github.com/IBM/fp-go/v2/context/readerioeither" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestWithTempFile(t *testing.T) { + + res := WithTempFile(onWriteAll[*os.File]([]byte("Carsten"))) + + assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())()) +} + +func TestWithTempFileOnClosedFile(t *testing.T) { + + res := WithTempFile(func(f *os.File) RIOE.ReaderIOEither[[]byte] { + return F.Pipe2( + f, + onWriteAll[*os.File]([]byte("Carsten")), + RIOE.ChainFirst(F.Constant1[[]byte](Close(f))), + ) + }) + + assert.Equal(t, E.Of[error]([]byte("Carsten")), res(context.Background())()) +} diff --git a/v2/context/readerioeither/file/write.go b/v2/context/readerioeither/file/write.go new file mode 100644 index 0000000..75c39f7 --- /dev/null +++ b/v2/context/readerioeither/file/write.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "context" + "io" + + RIOE "github.com/IBM/fp-go/v2/context/readerioeither" + F "github.com/IBM/fp-go/v2/function" +) + +func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] { + return func(w W) RIOE.ReaderIOEither[[]byte] { + return F.Pipe1( + RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) { + return func() ([]byte, error) { + _, err := w.Write(data) + return data, err + } + }), + RIOE.WithContext[[]byte], + ) + } +} + +// WriteAll uses a generator function to create a stream, writes data to it and closes it +func WriteAll[W io.WriteCloser](data []byte) func(acquire RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] { + onWrite := onWriteAll[W](data) + return func(onCreate RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] { + return RIOE.WithResource[[]byte]( + onCreate, + Close[W])( + onWrite, + ) + } +} + +// Write uses a generator function to create a stream, writes data to it and closes it +func Write[R any, W io.WriteCloser](acquire RIOE.ReaderIOEither[W]) func(use func(W) RIOE.ReaderIOEither[R]) RIOE.ReaderIOEither[R] { + return RIOE.WithResource[R]( + acquire, + Close[W]) +} diff --git a/v2/context/readerioeither/gen.go b/v2/context/readerioeither/gen.go new file mode 100644 index 0000000..2099fe4 --- /dev/null +++ b/v2/context/readerioeither/gen.go @@ -0,0 +1,594 @@ +package readerioeither + +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-05-24 22:24:01.4250895 +0200 CEST m=+0.014515801 + +import ( + "context" + + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// Eitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize0] +func Eitherize0[F ~func(context.Context) (R, error), R any](f F) func() ReaderIOEither[R] { + return G.Eitherize0[ReaderIOEither[R]](f) +} + +// Uneitherize0 converts a function with 1 parameters returning a [ReaderIOEither[R]] into a function with 0 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize0[F ~func() ReaderIOEither[R], R any](f F) func(context.Context) (R, error) { + return G.Uneitherize0[ReaderIOEither[R], func(context.Context) (R, error)](f) +} + +// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize1] +func Eitherize1[F ~func(context.Context, T0) (R, error), T0, R any](f F) func(T0) ReaderIOEither[R] { + return G.Eitherize1[ReaderIOEither[R]](f) +} + +// Uneitherize1 converts a function with 2 parameters returning a [ReaderIOEither[R]] into a function with 1 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize1[F ~func(T0) ReaderIOEither[R], T0, R any](f F) func(context.Context, T0) (R, error) { + return G.Uneitherize1[ReaderIOEither[R], func(context.Context, T0) (R, error)](f) +} + +// SequenceT1 converts 1 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func SequenceT1[T1 any](t1 ReaderIOEither[T1]) ReaderIOEither[T.Tuple1[T1]] { + return G.SequenceT1[ReaderIOEither[T.Tuple1[T1]]](t1) +} + +// SequenceSeqT1 converts 1 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func SequenceSeqT1[T1 any](t1 ReaderIOEither[T1]) ReaderIOEither[T.Tuple1[T1]] { + return G.SequenceSeqT1[ReaderIOEither[T.Tuple1[T1]]](t1) +} + +// SequenceParT1 converts 1 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func SequenceParT1[T1 any](t1 ReaderIOEither[T1]) ReaderIOEither[T.Tuple1[T1]] { + return G.SequenceParT1[ReaderIOEither[T.Tuple1[T1]]](t1) +} + +// SequenceTuple1 converts a [T.Tuple1] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func SequenceTuple1[T1 any](t T.Tuple1[ReaderIOEither[T1]]) ReaderIOEither[T.Tuple1[T1]] { + return G.SequenceTuple1[ReaderIOEither[T.Tuple1[T1]]](t) +} + +// SequenceSeqTuple1 converts a [T.Tuple1] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func SequenceSeqTuple1[T1 any](t T.Tuple1[ReaderIOEither[T1]]) ReaderIOEither[T.Tuple1[T1]] { + return G.SequenceSeqTuple1[ReaderIOEither[T.Tuple1[T1]]](t) +} + +// SequenceParTuple1 converts a [T.Tuple1] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func SequenceParTuple1[T1 any](t T.Tuple1[ReaderIOEither[T1]]) ReaderIOEither[T.Tuple1[T1]] { + return G.SequenceParTuple1[ReaderIOEither[T.Tuple1[T1]]](t) +} + +// TraverseTuple1 converts a [T.Tuple1] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func TraverseTuple1[F1 ~func(A1) ReaderIOEither[T1], A1, T1 any](f1 F1) func(T.Tuple1[A1]) ReaderIOEither[T.Tuple1[T1]] { + return G.TraverseTuple1[ReaderIOEither[T.Tuple1[T1]]](f1) +} + +// TraverseSeqTuple1 converts a [T.Tuple1] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func TraverseSeqTuple1[F1 ~func(A1) ReaderIOEither[T1], A1, T1 any](f1 F1) func(T.Tuple1[A1]) ReaderIOEither[T.Tuple1[T1]] { + return G.TraverseSeqTuple1[ReaderIOEither[T.Tuple1[T1]]](f1) +} + +// TraverseParTuple1 converts a [T.Tuple1] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple1]. +func TraverseParTuple1[F1 ~func(A1) ReaderIOEither[T1], A1, T1 any](f1 F1) func(T.Tuple1[A1]) ReaderIOEither[T.Tuple1[T1]] { + return G.TraverseParTuple1[ReaderIOEither[T.Tuple1[T1]]](f1) +} + +// Eitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize2] +func Eitherize2[F ~func(context.Context, T0, T1) (R, error), T0, T1, R any](f F) func(T0, T1) ReaderIOEither[R] { + return G.Eitherize2[ReaderIOEither[R]](f) +} + +// Uneitherize2 converts a function with 3 parameters returning a [ReaderIOEither[R]] into a function with 2 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize2[F ~func(T0, T1) ReaderIOEither[R], T0, T1, R any](f F) func(context.Context, T0, T1) (R, error) { + return G.Uneitherize2[ReaderIOEither[R], func(context.Context, T0, T1) (R, error)](f) +} + +// SequenceT2 converts 2 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func SequenceT2[T1, T2 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.SequenceT2[ReaderIOEither[T.Tuple2[T1, T2]]](t1, t2) +} + +// SequenceSeqT2 converts 2 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func SequenceSeqT2[T1, T2 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.SequenceSeqT2[ReaderIOEither[T.Tuple2[T1, T2]]](t1, t2) +} + +// SequenceParT2 converts 2 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func SequenceParT2[T1, T2 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.SequenceParT2[ReaderIOEither[T.Tuple2[T1, T2]]](t1, t2) +} + +// SequenceTuple2 converts a [T.Tuple2] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func SequenceTuple2[T1, T2 any](t T.Tuple2[ReaderIOEither[T1], ReaderIOEither[T2]]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.SequenceTuple2[ReaderIOEither[T.Tuple2[T1, T2]]](t) +} + +// SequenceSeqTuple2 converts a [T.Tuple2] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func SequenceSeqTuple2[T1, T2 any](t T.Tuple2[ReaderIOEither[T1], ReaderIOEither[T2]]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.SequenceSeqTuple2[ReaderIOEither[T.Tuple2[T1, T2]]](t) +} + +// SequenceParTuple2 converts a [T.Tuple2] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func SequenceParTuple2[T1, T2 any](t T.Tuple2[ReaderIOEither[T1], ReaderIOEither[T2]]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.SequenceParTuple2[ReaderIOEither[T.Tuple2[T1, T2]]](t) +} + +// TraverseTuple2 converts a [T.Tuple2] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func TraverseTuple2[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], A1, T1, A2, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.TraverseTuple2[ReaderIOEither[T.Tuple2[T1, T2]]](f1, f2) +} + +// TraverseSeqTuple2 converts a [T.Tuple2] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func TraverseSeqTuple2[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], A1, T1, A2, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.TraverseSeqTuple2[ReaderIOEither[T.Tuple2[T1, T2]]](f1, f2) +} + +// TraverseParTuple2 converts a [T.Tuple2] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple2]. +func TraverseParTuple2[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], A1, T1, A2, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) ReaderIOEither[T.Tuple2[T1, T2]] { + return G.TraverseParTuple2[ReaderIOEither[T.Tuple2[T1, T2]]](f1, f2) +} + +// Eitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize3] +func Eitherize3[F ~func(context.Context, T0, T1, T2) (R, error), T0, T1, T2, R any](f F) func(T0, T1, T2) ReaderIOEither[R] { + return G.Eitherize3[ReaderIOEither[R]](f) +} + +// Uneitherize3 converts a function with 4 parameters returning a [ReaderIOEither[R]] into a function with 3 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize3[F ~func(T0, T1, T2) ReaderIOEither[R], T0, T1, T2, R any](f F) func(context.Context, T0, T1, T2) (R, error) { + return G.Uneitherize3[ReaderIOEither[R], func(context.Context, T0, T1, T2) (R, error)](f) +} + +// SequenceT3 converts 3 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func SequenceT3[T1, T2, T3 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.SequenceT3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](t1, t2, t3) +} + +// SequenceSeqT3 converts 3 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func SequenceSeqT3[T1, T2, T3 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.SequenceSeqT3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](t1, t2, t3) +} + +// SequenceParT3 converts 3 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func SequenceParT3[T1, T2, T3 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.SequenceParT3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](t1, t2, t3) +} + +// SequenceTuple3 converts a [T.Tuple3] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func SequenceTuple3[T1, T2, T3 any](t T.Tuple3[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3]]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.SequenceTuple3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](t) +} + +// SequenceSeqTuple3 converts a [T.Tuple3] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func SequenceSeqTuple3[T1, T2, T3 any](t T.Tuple3[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3]]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.SequenceSeqTuple3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](t) +} + +// SequenceParTuple3 converts a [T.Tuple3] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func SequenceParTuple3[T1, T2, T3 any](t T.Tuple3[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3]]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.SequenceParTuple3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](t) +} + +// TraverseTuple3 converts a [T.Tuple3] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func TraverseTuple3[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], A1, T1, A2, T2, A3, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.TraverseTuple3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](f1, f2, f3) +} + +// TraverseSeqTuple3 converts a [T.Tuple3] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func TraverseSeqTuple3[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], A1, T1, A2, T2, A3, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.TraverseSeqTuple3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](f1, f2, f3) +} + +// TraverseParTuple3 converts a [T.Tuple3] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple3]. +func TraverseParTuple3[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], A1, T1, A2, T2, A3, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) ReaderIOEither[T.Tuple3[T1, T2, T3]] { + return G.TraverseParTuple3[ReaderIOEither[T.Tuple3[T1, T2, T3]]](f1, f2, f3) +} + +// Eitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize4] +func Eitherize4[F ~func(context.Context, T0, T1, T2, T3) (R, error), T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[R] { + return G.Eitherize4[ReaderIOEither[R]](f) +} + +// Uneitherize4 converts a function with 5 parameters returning a [ReaderIOEither[R]] into a function with 4 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize4[F ~func(T0, T1, T2, T3) ReaderIOEither[R], T0, T1, T2, T3, R any](f F) func(context.Context, T0, T1, T2, T3) (R, error) { + return G.Uneitherize4[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3) (R, error)](f) +} + +// SequenceT4 converts 4 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func SequenceT4[T1, T2, T3, T4 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceT4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](t1, t2, t3, t4) +} + +// SequenceSeqT4 converts 4 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func SequenceSeqT4[T1, T2, T3, T4 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceSeqT4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](t1, t2, t3, t4) +} + +// SequenceParT4 converts 4 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func SequenceParT4[T1, T2, T3, T4 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceParT4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](t1, t2, t3, t4) +} + +// SequenceTuple4 converts a [T.Tuple4] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func SequenceTuple4[T1, T2, T3, T4 any](t T.Tuple4[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4]]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceTuple4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](t) +} + +// SequenceSeqTuple4 converts a [T.Tuple4] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func SequenceSeqTuple4[T1, T2, T3, T4 any](t T.Tuple4[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4]]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceSeqTuple4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](t) +} + +// SequenceParTuple4 converts a [T.Tuple4] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func SequenceParTuple4[T1, T2, T3, T4 any](t T.Tuple4[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4]]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceParTuple4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](t) +} + +// TraverseTuple4 converts a [T.Tuple4] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func TraverseTuple4[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], A1, T1, A2, T2, A3, T3, A4, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.TraverseTuple4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](f1, f2, f3, f4) +} + +// TraverseSeqTuple4 converts a [T.Tuple4] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func TraverseSeqTuple4[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], A1, T1, A2, T2, A3, T3, A4, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.TraverseSeqTuple4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](f1, f2, f3, f4) +} + +// TraverseParTuple4 converts a [T.Tuple4] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple4]. +func TraverseParTuple4[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], A1, T1, A2, T2, A3, T3, A4, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) ReaderIOEither[T.Tuple4[T1, T2, T3, T4]] { + return G.TraverseParTuple4[ReaderIOEither[T.Tuple4[T1, T2, T3, T4]]](f1, f2, f3, f4) +} + +// Eitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize5] +func Eitherize5[F ~func(context.Context, T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[R] { + return G.Eitherize5[ReaderIOEither[R]](f) +} + +// Uneitherize5 converts a function with 6 parameters returning a [ReaderIOEither[R]] into a function with 5 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize5[F ~func(T0, T1, T2, T3, T4) ReaderIOEither[R], T0, T1, T2, T3, T4, R any](f F) func(context.Context, T0, T1, T2, T3, T4) (R, error) { + return G.Uneitherize5[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4) (R, error)](f) +} + +// SequenceT5 converts 5 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func SequenceT5[T1, T2, T3, T4, T5 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceT5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](t1, t2, t3, t4, t5) +} + +// SequenceSeqT5 converts 5 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func SequenceSeqT5[T1, T2, T3, T4, T5 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceSeqT5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](t1, t2, t3, t4, t5) +} + +// SequenceParT5 converts 5 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func SequenceParT5[T1, T2, T3, T4, T5 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceParT5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](t1, t2, t3, t4, t5) +} + +// SequenceTuple5 converts a [T.Tuple5] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func SequenceTuple5[T1, T2, T3, T4, T5 any](t T.Tuple5[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5]]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceTuple5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](t) +} + +// SequenceSeqTuple5 converts a [T.Tuple5] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func SequenceSeqTuple5[T1, T2, T3, T4, T5 any](t T.Tuple5[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5]]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceSeqTuple5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](t) +} + +// SequenceParTuple5 converts a [T.Tuple5] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func SequenceParTuple5[T1, T2, T3, T4, T5 any](t T.Tuple5[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5]]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceParTuple5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](t) +} + +// TraverseTuple5 converts a [T.Tuple5] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func TraverseTuple5[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.TraverseTuple5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](f1, f2, f3, f4, f5) +} + +// TraverseSeqTuple5 converts a [T.Tuple5] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func TraverseSeqTuple5[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.TraverseSeqTuple5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](f1, f2, f3, f4, f5) +} + +// TraverseParTuple5 converts a [T.Tuple5] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple5]. +func TraverseParTuple5[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.TraverseParTuple5[ReaderIOEither[T.Tuple5[T1, T2, T3, T4, T5]]](f1, f2, f3, f4, f5) +} + +// Eitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize6] +func Eitherize6[F ~func(context.Context, T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[R] { + return G.Eitherize6[ReaderIOEither[R]](f) +} + +// Uneitherize6 converts a function with 7 parameters returning a [ReaderIOEither[R]] into a function with 6 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5) (R, error) { + return G.Uneitherize6[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5) (R, error)](f) +} + +// SequenceT6 converts 6 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func SequenceT6[T1, T2, T3, T4, T5, T6 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceT6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](t1, t2, t3, t4, t5, t6) +} + +// SequenceSeqT6 converts 6 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func SequenceSeqT6[T1, T2, T3, T4, T5, T6 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceSeqT6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](t1, t2, t3, t4, t5, t6) +} + +// SequenceParT6 converts 6 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func SequenceParT6[T1, T2, T3, T4, T5, T6 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceParT6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](t1, t2, t3, t4, t5, t6) +} + +// SequenceTuple6 converts a [T.Tuple6] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func SequenceTuple6[T1, T2, T3, T4, T5, T6 any](t T.Tuple6[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6]]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceTuple6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](t) +} + +// SequenceSeqTuple6 converts a [T.Tuple6] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func SequenceSeqTuple6[T1, T2, T3, T4, T5, T6 any](t T.Tuple6[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6]]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceSeqTuple6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](t) +} + +// SequenceParTuple6 converts a [T.Tuple6] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func SequenceParTuple6[T1, T2, T3, T4, T5, T6 any](t T.Tuple6[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6]]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceParTuple6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](t) +} + +// TraverseTuple6 converts a [T.Tuple6] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func TraverseTuple6[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.TraverseTuple6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](f1, f2, f3, f4, f5, f6) +} + +// TraverseSeqTuple6 converts a [T.Tuple6] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func TraverseSeqTuple6[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.TraverseSeqTuple6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](f1, f2, f3, f4, f5, f6) +} + +// TraverseParTuple6 converts a [T.Tuple6] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple6]. +func TraverseParTuple6[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.TraverseParTuple6[ReaderIOEither[T.Tuple6[T1, T2, T3, T4, T5, T6]]](f1, f2, f3, f4, f5, f6) +} + +// Eitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize7] +func Eitherize7[F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[R] { + return G.Eitherize7[ReaderIOEither[R]](f) +} + +// Uneitherize7 converts a function with 8 parameters returning a [ReaderIOEither[R]] into a function with 7 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, T6, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error) { + return G.Uneitherize7[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error)](f) +} + +// SequenceT7 converts 7 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func SequenceT7[T1, T2, T3, T4, T5, T6, T7 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceT7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](t1, t2, t3, t4, t5, t6, t7) +} + +// SequenceSeqT7 converts 7 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func SequenceSeqT7[T1, T2, T3, T4, T5, T6, T7 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceSeqT7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](t1, t2, t3, t4, t5, t6, t7) +} + +// SequenceParT7 converts 7 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func SequenceParT7[T1, T2, T3, T4, T5, T6, T7 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceParT7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](t1, t2, t3, t4, t5, t6, t7) +} + +// SequenceTuple7 converts a [T.Tuple7] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func SequenceTuple7[T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7]]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceTuple7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](t) +} + +// SequenceSeqTuple7 converts a [T.Tuple7] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func SequenceSeqTuple7[T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7]]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceSeqTuple7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](t) +} + +// SequenceParTuple7 converts a [T.Tuple7] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func SequenceParTuple7[T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7]]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceParTuple7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](t) +} + +// TraverseTuple7 converts a [T.Tuple7] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func TraverseTuple7[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.TraverseTuple7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](f1, f2, f3, f4, f5, f6, f7) +} + +// TraverseSeqTuple7 converts a [T.Tuple7] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func TraverseSeqTuple7[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.TraverseSeqTuple7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](f1, f2, f3, f4, f5, f6, f7) +} + +// TraverseParTuple7 converts a [T.Tuple7] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple7]. +func TraverseParTuple7[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.TraverseParTuple7[ReaderIOEither[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](f1, f2, f3, f4, f5, f6, f7) +} + +// Eitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize8] +func Eitherize8[F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[R] { + return G.Eitherize8[ReaderIOEither[R]](f) +} + +// Uneitherize8 converts a function with 9 parameters returning a [ReaderIOEither[R]] into a function with 8 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error) { + return G.Uneitherize8[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error)](f) +} + +// SequenceT8 converts 8 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func SequenceT8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceT8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](t1, t2, t3, t4, t5, t6, t7, t8) +} + +// SequenceSeqT8 converts 8 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func SequenceSeqT8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceSeqT8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](t1, t2, t3, t4, t5, t6, t7, t8) +} + +// SequenceParT8 converts 8 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func SequenceParT8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceParT8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](t1, t2, t3, t4, t5, t6, t7, t8) +} + +// SequenceTuple8 converts a [T.Tuple8] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func SequenceTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8]]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceTuple8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](t) +} + +// SequenceSeqTuple8 converts a [T.Tuple8] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func SequenceSeqTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8]]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceSeqTuple8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](t) +} + +// SequenceParTuple8 converts a [T.Tuple8] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func SequenceParTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8]]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceParTuple8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](t) +} + +// TraverseTuple8 converts a [T.Tuple8] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func TraverseTuple8[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.TraverseTuple8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](f1, f2, f3, f4, f5, f6, f7, f8) +} + +// TraverseSeqTuple8 converts a [T.Tuple8] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func TraverseSeqTuple8[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.TraverseSeqTuple8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](f1, f2, f3, f4, f5, f6, f7, f8) +} + +// TraverseParTuple8 converts a [T.Tuple8] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple8]. +func TraverseParTuple8[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.TraverseParTuple8[ReaderIOEither[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](f1, f2, f3, f4, f5, f6, f7, f8) +} + +// Eitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize9] +func Eitherize9[F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[R] { + return G.Eitherize9[ReaderIOEither[R]](f) +} + +// Uneitherize9 converts a function with 10 parameters returning a [ReaderIOEither[R]] into a function with 9 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error) { + return G.Uneitherize9[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error)](f) +} + +// SequenceT9 converts 9 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func SequenceT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceT9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](t1, t2, t3, t4, t5, t6, t7, t8, t9) +} + +// SequenceSeqT9 converts 9 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func SequenceSeqT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceSeqT9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](t1, t2, t3, t4, t5, t6, t7, t8, t9) +} + +// SequenceParT9 converts 9 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func SequenceParT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceParT9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](t1, t2, t3, t4, t5, t6, t7, t8, t9) +} + +// SequenceTuple9 converts a [T.Tuple9] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func SequenceTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8], ReaderIOEither[T9]]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceTuple9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](t) +} + +// SequenceSeqTuple9 converts a [T.Tuple9] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func SequenceSeqTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8], ReaderIOEither[T9]]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceSeqTuple9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](t) +} + +// SequenceParTuple9 converts a [T.Tuple9] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func SequenceParTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8], ReaderIOEither[T9]]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceParTuple9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](t) +} + +// TraverseTuple9 converts a [T.Tuple9] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func TraverseTuple9[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], F9 ~func(A9) ReaderIOEither[T9], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.TraverseTuple9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](f1, f2, f3, f4, f5, f6, f7, f8, f9) +} + +// TraverseSeqTuple9 converts a [T.Tuple9] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func TraverseSeqTuple9[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], F9 ~func(A9) ReaderIOEither[T9], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.TraverseSeqTuple9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](f1, f2, f3, f4, f5, f6, f7, f8, f9) +} + +// TraverseParTuple9 converts a [T.Tuple9] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple9]. +func TraverseParTuple9[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], F9 ~func(A9) ReaderIOEither[T9], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.TraverseParTuple9[ReaderIOEither[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](f1, f2, f3, f4, f5, f6, f7, f8, f9) +} + +// Eitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[R]] +// The inverse function is [Uneitherize10] +func Eitherize10[F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[R] { + return G.Eitherize10[ReaderIOEither[R]](f) +} + +// Uneitherize10 converts a function with 11 parameters returning a [ReaderIOEither[R]] into a function with 10 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) { + return G.Uneitherize10[ReaderIOEither[R], func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error)](f) +} + +// SequenceT10 converts 10 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func SequenceT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9], t10 ReaderIOEither[T10]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceT10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) +} + +// SequenceSeqT10 converts 10 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func SequenceSeqT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9], t10 ReaderIOEither[T10]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceSeqT10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) +} + +// SequenceParT10 converts 10 [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func SequenceParT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 ReaderIOEither[T1], t2 ReaderIOEither[T2], t3 ReaderIOEither[T3], t4 ReaderIOEither[T4], t5 ReaderIOEither[T5], t6 ReaderIOEither[T6], t7 ReaderIOEither[T7], t8 ReaderIOEither[T8], t9 ReaderIOEither[T9], t10 ReaderIOEither[T10]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceParT10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) +} + +// SequenceTuple10 converts a [T.Tuple10] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func SequenceTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8], ReaderIOEither[T9], ReaderIOEither[T10]]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceTuple10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](t) +} + +// SequenceSeqTuple10 converts a [T.Tuple10] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func SequenceSeqTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8], ReaderIOEither[T9], ReaderIOEither[T10]]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceSeqTuple10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](t) +} + +// SequenceParTuple10 converts a [T.Tuple10] of [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func SequenceParTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[ReaderIOEither[T1], ReaderIOEither[T2], ReaderIOEither[T3], ReaderIOEither[T4], ReaderIOEither[T5], ReaderIOEither[T6], ReaderIOEither[T7], ReaderIOEither[T8], ReaderIOEither[T9], ReaderIOEither[T10]]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceParTuple10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](t) +} + +// TraverseTuple10 converts a [T.Tuple10] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func TraverseTuple10[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], F9 ~func(A9) ReaderIOEither[T9], F10 ~func(A10) ReaderIOEither[T10], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.TraverseTuple10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) +} + +// TraverseSeqTuple10 converts a [T.Tuple10] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func TraverseSeqTuple10[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], F9 ~func(A9) ReaderIOEither[T9], F10 ~func(A10) ReaderIOEither[T10], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.TraverseSeqTuple10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) +} + +// TraverseParTuple10 converts a [T.Tuple10] of [A] via transformer functions transforming [A] to a [ReaderIOEither] into a [ReaderIOEither] of a [T.Tuple10]. +func TraverseParTuple10[F1 ~func(A1) ReaderIOEither[T1], F2 ~func(A2) ReaderIOEither[T2], F3 ~func(A3) ReaderIOEither[T3], F4 ~func(A4) ReaderIOEither[T4], F5 ~func(A5) ReaderIOEither[T5], F6 ~func(A6) ReaderIOEither[T6], F7 ~func(A7) ReaderIOEither[T7], F8 ~func(A8) ReaderIOEither[T8], F9 ~func(A9) ReaderIOEither[T9], F10 ~func(A10) ReaderIOEither[T10], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.TraverseParTuple10[ReaderIOEither[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) +} diff --git a/v2/context/readerioeither/generic/bind.go b/v2/context/readerioeither/generic/bind.go new file mode 100644 index 0000000..095c2f1 --- /dev/null +++ b/v2/context/readerioeither/generic/bind.go @@ -0,0 +1,92 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + ET "github.com/IBM/fp-go/v2/either" + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GRS ~func(context.Context) GS, GS ~func() ET.Either[error, S], S any]( + empty S, +) GRS { + return Of[GRS, GS, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GRT ~func(context.Context) GT, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], GT ~func() ET.Either[error, T], S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GRT, +) func(GRS1) GRS2 { + return C.Bind( + Chain[GRS1, GRS2, GS1, GS2, S1, S2], + Map[GRT, GRS2, GT, GS2, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GRS1) GRS2 { + return F.Let( + Map[GRS1, GRS2, GS1, GS2, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GRS1) GRS2 { + return F.LetTo( + Map[GRS1, GRS2, GS1, GS2, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GRS1 ~func(context.Context) GS1, GRT ~func(context.Context) GT, GS1 ~func() ET.Either[error, S1], GT ~func() ET.Either[error, T], S1, T any]( + setter func(T) S1, +) func(GRT) GRS1 { + return C.BindTo( + Map[GRT, GRS1, GT, GS1, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GRTS1 ~func(context.Context) GTS1, GRS1 ~func(context.Context) GS1, GRS2 ~func(context.Context) GS2, GRT ~func(context.Context) GT, GTS1 ~func() ET.Either[error, func(T) S2], GS1 ~func() ET.Either[error, S1], GS2 ~func() ET.Either[error, S2], GT ~func() ET.Either[error, T], S1, S2, T any]( + setter func(T) func(S1) S2, + fa GRT, +) func(GRS1) GRS2 { + return A.ApS( + Ap[GRS2, GRTS1, GRT, GS2, GTS1, GT], + Map[GRS1, GRTS1, GS1, GTS1, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/context/readerioeither/generic/bracket.go b/v2/context/readerioeither/generic/bracket.go new file mode 100644 index 0000000..66f0cd5 --- /dev/null +++ b/v2/context/readerioeither/generic/bracket.go @@ -0,0 +1,53 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/internal/bracket" + I "github.com/IBM/fp-go/v2/readerio/generic" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[ + GA ~func(context.Context) TA, + GB ~func(context.Context) TB, + GANY ~func(context.Context) TANY, + + TA ~func() E.Either[error, A], + TB ~func() E.Either[error, B], + TANY ~func() E.Either[error, ANY], + + A, B, ANY any]( + + acquire GA, + use func(A) GB, + release func(A, E.Either[error, B]) GANY, +) GB { + return G.Bracket[GA, GB, GANY, E.Either[error, B], A, B]( + I.Of[GB, TB, context.Context, E.Either[error, B]], + MonadChain[GA, GB, TA, TB, A, B], + I.MonadChain[GB, GB, TB, TB, context.Context, E.Either[error, B], E.Either[error, B]], + MonadChain[GANY, GB, TANY, TB, ANY, B], + + acquire, + use, + release, + ) +} diff --git a/v2/context/readerioeither/generic/cancel.go b/v2/context/readerioeither/generic/cancel.go new file mode 100644 index 0000000..e827d36 --- /dev/null +++ b/v2/context/readerioeither/generic/cancel.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + CIOE "github.com/IBM/fp-go/v2/context/ioeither/generic" + E "github.com/IBM/fp-go/v2/either" + IOE "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// withContext wraps an existing ReaderIOEither and performs a context check for cancellation before delegating +func WithContext[GRA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](ma GRA) GRA { + return func(ctx context.Context) GIOA { + if err := context.Cause(ctx); err != nil { + return IOE.Left[GIOA](err) + } + return CIOE.WithContext(ctx, ma(ctx)) + } +} diff --git a/v2/context/readerioeither/generic/eq.go b/v2/context/readerioeither/generic/eq.go new file mode 100644 index 0000000..9846837 --- /dev/null +++ b/v2/context/readerioeither/generic/eq.go @@ -0,0 +1,29 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// Eq implements the equals predicate for values contained in the IOEither monad +func Eq[GRA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](eq EQ.Eq[E.Either[error, A]]) func(context.Context) EQ.Eq[GRA] { + return G.Eq[GRA](eq) +} diff --git a/v2/context/readerioeither/generic/gen.go b/v2/context/readerioeither/generic/gen.go new file mode 100644 index 0000000..cb16622 --- /dev/null +++ b/v2/context/readerioeither/generic/gen.go @@ -0,0 +1,3843 @@ +package generic + +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-05-24 22:24:01.4250895 +0200 CEST m=+0.014515801 + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + A "github.com/IBM/fp-go/v2/internal/apply" + RE "github.com/IBM/fp-go/v2/readerioeither/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// Eitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning a [GRA] +// The inverse function is [Uneitherize0] +func Eitherize0[GRA ~func(context.Context) GIOA, F ~func(context.Context) (R, error), GIOA ~func() E.Either[error, R], R any](f F) func() GRA { + return RE.Eitherize0[GRA](f) +} + +// Uneitherize0 converts a function with 0 parameters returning a [GRA] into a function with 0 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize0[GRA ~func(context.Context) GIOA, F ~func(context.Context) (R, error), GIOA ~func() E.Either[error, R], R any](f func() GRA) F { + return func(c context.Context) (R, error) { + return E.UnwrapError(f()(c)()) + } +} + +// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [GRA] +// The inverse function is [Uneitherize1] +func Eitherize1[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0) (R, error), GIOA ~func() E.Either[error, R], T0, R any](f F) func(T0) GRA { + return RE.Eitherize1[GRA](f) +} + +// Uneitherize1 converts a function with 1 parameters returning a [GRA] into a function with 1 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize1[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0) (R, error), GIOA ~func() E.Either[error, R], T0, R any](f func(T0) GRA) F { + return func(c context.Context, t0 T0) (R, error) { + return E.UnwrapError(f(t0)(c)()) + } +} + +// SequenceT1 converts 1 readers into a reader of a [T.Tuple1]. +func SequenceT1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + T1 any]( + t1 GR_T1, +) GR_TUPLE1 { + return A.SequenceT1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + t1, + ) +} + +// SequenceSeqT1 converts 1 readers into a reader of a [T.Tuple1]. +func SequenceSeqT1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + T1 any]( + t1 GR_T1, +) GR_TUPLE1 { + return A.SequenceT1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + t1, + ) +} + +// SequenceParT1 converts 1 readers into a reader of a [T.Tuple1]. +func SequenceParT1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + T1 any]( + t1 GR_T1, +) GR_TUPLE1 { + return A.SequenceT1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + t1, + ) +} + +// SequenceTuple1 converts a [T.Tuple1] of readers into a reader of a [T.Tuple1]. +func SequenceTuple1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + T1 any](t T.Tuple1[GR_T1]) GR_TUPLE1 { + return A.SequenceTuple1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + t, + ) +} + +// SequenceSeqTuple1 converts a [T.Tuple1] of readers into a reader of a [T.Tuple1]. +func SequenceSeqTuple1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + T1 any](t T.Tuple1[GR_T1]) GR_TUPLE1 { + return A.SequenceTuple1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + t, + ) +} + +// SequenceParTuple1 converts a [T.Tuple1] of readers into a reader of a [T.Tuple1]. +func SequenceParTuple1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + T1 any](t T.Tuple1[GR_T1]) GR_TUPLE1 { + return A.SequenceTuple1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + t, + ) +} + +// TraverseTuple1 converts a [T.Tuple1] of readers into a reader of a [T.Tuple1]. +func TraverseTuple1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + F1 ~func(A1) GR_T1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + A1, + T1 any](f1 F1) func(T.Tuple1[A1]) GR_TUPLE1 { + return func(t T.Tuple1[A1]) GR_TUPLE1 { + return A.TraverseTuple1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + f1, + t, + ) + } +} + +// TraverseSeqTuple1 converts a [T.Tuple1] of readers into a reader of a [T.Tuple1]. +func TraverseSeqTuple1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + F1 ~func(A1) GR_T1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + A1, + T1 any](f1 F1) func(T.Tuple1[A1]) GR_TUPLE1 { + return func(t T.Tuple1[A1]) GR_TUPLE1 { + return A.TraverseTuple1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + f1, + t, + ) + } +} + +// TraverseParTuple1 converts a [T.Tuple1] of readers into a reader of a [T.Tuple1]. +func TraverseParTuple1[ + GR_TUPLE1 ~func(context.Context) GIO_TUPLE1, + F1 ~func(A1) GR_T1, + GR_T1 ~func(context.Context) GIO_T1, + GIO_TUPLE1 ~func() E.Either[error, T.Tuple1[T1]], + GIO_T1 ~func() E.Either[error, T1], + A1, + T1 any](f1 F1) func(T.Tuple1[A1]) GR_TUPLE1 { + return func(t T.Tuple1[A1]) GR_TUPLE1 { + return A.TraverseTuple1( + Map[GR_T1, GR_TUPLE1, GIO_T1], + f1, + t, + ) + } +} + +// Eitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning a [GRA] +// The inverse function is [Uneitherize2] +func Eitherize2[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1) (R, error), GIOA ~func() E.Either[error, R], T0, T1, R any](f F) func(T0, T1) GRA { + return RE.Eitherize2[GRA](f) +} + +// Uneitherize2 converts a function with 2 parameters returning a [GRA] into a function with 2 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize2[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1) (R, error), GIOA ~func() E.Either[error, R], T0, T1, R any](f func(T0, T1) GRA) F { + return func(c context.Context, t0 T0, t1 T1) (R, error) { + return E.UnwrapError(f(t0, t1)(c)()) + } +} + +// SequenceT2 converts 2 readers into a reader of a [T.Tuple2]. +func SequenceT2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + T1, + T2 any]( + t1 GR_T1, + t2 GR_T2, +) GR_TUPLE2 { + return A.SequenceT2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + Ap[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + t1, + t2, + ) +} + +// SequenceSeqT2 converts 2 readers into a reader of a [T.Tuple2]. +func SequenceSeqT2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + T1, + T2 any]( + t1 GR_T1, + t2 GR_T2, +) GR_TUPLE2 { + return A.SequenceT2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + ApSeq[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + t1, + t2, + ) +} + +// SequenceParT2 converts 2 readers into a reader of a [T.Tuple2]. +func SequenceParT2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + T1, + T2 any]( + t1 GR_T1, + t2 GR_T2, +) GR_TUPLE2 { + return A.SequenceT2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + ApPar[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + t1, + t2, + ) +} + +// SequenceTuple2 converts a [T.Tuple2] of readers into a reader of a [T.Tuple2]. +func SequenceTuple2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + T1, + T2 any](t T.Tuple2[GR_T1, GR_T2]) GR_TUPLE2 { + return A.SequenceTuple2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + Ap[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + t, + ) +} + +// SequenceSeqTuple2 converts a [T.Tuple2] of readers into a reader of a [T.Tuple2]. +func SequenceSeqTuple2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + T1, + T2 any](t T.Tuple2[GR_T1, GR_T2]) GR_TUPLE2 { + return A.SequenceTuple2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + ApSeq[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + t, + ) +} + +// SequenceParTuple2 converts a [T.Tuple2] of readers into a reader of a [T.Tuple2]. +func SequenceParTuple2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + T1, + T2 any](t T.Tuple2[GR_T1, GR_T2]) GR_TUPLE2 { + return A.SequenceTuple2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + ApPar[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + t, + ) +} + +// TraverseTuple2 converts a [T.Tuple2] of readers into a reader of a [T.Tuple2]. +func TraverseTuple2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + A1, + T1, + A2, + T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) GR_TUPLE2 { + return func(t T.Tuple2[A1, A2]) GR_TUPLE2 { + return A.TraverseTuple2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + Ap[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + f1, + f2, + t, + ) + } +} + +// TraverseSeqTuple2 converts a [T.Tuple2] of readers into a reader of a [T.Tuple2]. +func TraverseSeqTuple2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + A1, + T1, + A2, + T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) GR_TUPLE2 { + return func(t T.Tuple2[A1, A2]) GR_TUPLE2 { + return A.TraverseTuple2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + ApSeq[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + f1, + f2, + t, + ) + } +} + +// TraverseParTuple2 converts a [T.Tuple2] of readers into a reader of a [T.Tuple2]. +func TraverseParTuple2[ + GR_TUPLE2 ~func(context.Context) GIO_TUPLE2, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GIO_TUPLE2 ~func() E.Either[error, T.Tuple2[T1, T2]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + A1, + T1, + A2, + T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) GR_TUPLE2 { + return func(t T.Tuple2[A1, A2]) GR_TUPLE2 { + return A.TraverseTuple2( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GIO_T1], + ApPar[GR_TUPLE2, func(context.Context) func() E.Either[error, func(T2) T.Tuple2[T1, T2]], GR_T2], + f1, + f2, + t, + ) + } +} + +// Eitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning a [GRA] +// The inverse function is [Uneitherize3] +func Eitherize3[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, R any](f F) func(T0, T1, T2) GRA { + return RE.Eitherize3[GRA](f) +} + +// Uneitherize3 converts a function with 3 parameters returning a [GRA] into a function with 3 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize3[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, R any](f func(T0, T1, T2) GRA) F { + return func(c context.Context, t0 T0, t1 T1, t2 T2) (R, error) { + return E.UnwrapError(f(t0, t1, t2)(c)()) + } +} + +// SequenceT3 converts 3 readers into a reader of a [T.Tuple3]. +func SequenceT3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + T1, + T2, + T3 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, +) GR_TUPLE3 { + return A.SequenceT3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + Ap[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + t1, + t2, + t3, + ) +} + +// SequenceSeqT3 converts 3 readers into a reader of a [T.Tuple3]. +func SequenceSeqT3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + T1, + T2, + T3 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, +) GR_TUPLE3 { + return A.SequenceT3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + ApSeq[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + t1, + t2, + t3, + ) +} + +// SequenceParT3 converts 3 readers into a reader of a [T.Tuple3]. +func SequenceParT3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + T1, + T2, + T3 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, +) GR_TUPLE3 { + return A.SequenceT3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + ApPar[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + t1, + t2, + t3, + ) +} + +// SequenceTuple3 converts a [T.Tuple3] of readers into a reader of a [T.Tuple3]. +func SequenceTuple3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + T1, + T2, + T3 any](t T.Tuple3[GR_T1, GR_T2, GR_T3]) GR_TUPLE3 { + return A.SequenceTuple3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + Ap[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + t, + ) +} + +// SequenceSeqTuple3 converts a [T.Tuple3] of readers into a reader of a [T.Tuple3]. +func SequenceSeqTuple3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + T1, + T2, + T3 any](t T.Tuple3[GR_T1, GR_T2, GR_T3]) GR_TUPLE3 { + return A.SequenceTuple3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + ApSeq[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + t, + ) +} + +// SequenceParTuple3 converts a [T.Tuple3] of readers into a reader of a [T.Tuple3]. +func SequenceParTuple3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + T1, + T2, + T3 any](t T.Tuple3[GR_T1, GR_T2, GR_T3]) GR_TUPLE3 { + return A.SequenceTuple3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + ApPar[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + t, + ) +} + +// TraverseTuple3 converts a [T.Tuple3] of readers into a reader of a [T.Tuple3]. +func TraverseTuple3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + A1, + T1, + A2, + T2, + A3, + T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) GR_TUPLE3 { + return func(t T.Tuple3[A1, A2, A3]) GR_TUPLE3 { + return A.TraverseTuple3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + Ap[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + f1, + f2, + f3, + t, + ) + } +} + +// TraverseSeqTuple3 converts a [T.Tuple3] of readers into a reader of a [T.Tuple3]. +func TraverseSeqTuple3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + A1, + T1, + A2, + T2, + A3, + T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) GR_TUPLE3 { + return func(t T.Tuple3[A1, A2, A3]) GR_TUPLE3 { + return A.TraverseTuple3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + ApSeq[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + f1, + f2, + f3, + t, + ) + } +} + +// TraverseParTuple3 converts a [T.Tuple3] of readers into a reader of a [T.Tuple3]. +func TraverseParTuple3[ + GR_TUPLE3 ~func(context.Context) GIO_TUPLE3, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GIO_TUPLE3 ~func() E.Either[error, T.Tuple3[T1, T2, T3]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + A1, + T1, + A2, + T2, + A3, + T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) GR_TUPLE3 { + return func(t T.Tuple3[A1, A2, A3]) GR_TUPLE3 { + return A.TraverseTuple3( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], func(context.Context) func() E.Either[error, func(T2) func(T3) T.Tuple3[T1, T2, T3]], GR_T2], + ApPar[GR_TUPLE3, func(context.Context) func() E.Either[error, func(T3) T.Tuple3[T1, T2, T3]], GR_T3], + f1, + f2, + f3, + t, + ) + } +} + +// Eitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning a [GRA] +// The inverse function is [Uneitherize4] +func Eitherize4[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) GRA { + return RE.Eitherize4[GRA](f) +} + +// Uneitherize4 converts a function with 4 parameters returning a [GRA] into a function with 4 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize4[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, R any](f func(T0, T1, T2, T3) GRA) F { + return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3)(c)()) + } +} + +// SequenceT4 converts 4 readers into a reader of a [T.Tuple4]. +func SequenceT4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + T1, + T2, + T3, + T4 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, +) GR_TUPLE4 { + return A.SequenceT4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + Ap[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceSeqT4 converts 4 readers into a reader of a [T.Tuple4]. +func SequenceSeqT4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + T1, + T2, + T3, + T4 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, +) GR_TUPLE4 { + return A.SequenceT4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + ApSeq[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceParT4 converts 4 readers into a reader of a [T.Tuple4]. +func SequenceParT4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + T1, + T2, + T3, + T4 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, +) GR_TUPLE4 { + return A.SequenceT4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + ApPar[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceTuple4 converts a [T.Tuple4] of readers into a reader of a [T.Tuple4]. +func SequenceTuple4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + T1, + T2, + T3, + T4 any](t T.Tuple4[GR_T1, GR_T2, GR_T3, GR_T4]) GR_TUPLE4 { + return A.SequenceTuple4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + Ap[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + t, + ) +} + +// SequenceSeqTuple4 converts a [T.Tuple4] of readers into a reader of a [T.Tuple4]. +func SequenceSeqTuple4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + T1, + T2, + T3, + T4 any](t T.Tuple4[GR_T1, GR_T2, GR_T3, GR_T4]) GR_TUPLE4 { + return A.SequenceTuple4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + ApSeq[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + t, + ) +} + +// SequenceParTuple4 converts a [T.Tuple4] of readers into a reader of a [T.Tuple4]. +func SequenceParTuple4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + T1, + T2, + T3, + T4 any](t T.Tuple4[GR_T1, GR_T2, GR_T3, GR_T4]) GR_TUPLE4 { + return A.SequenceTuple4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + ApPar[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + t, + ) +} + +// TraverseTuple4 converts a [T.Tuple4] of readers into a reader of a [T.Tuple4]. +func TraverseTuple4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) GR_TUPLE4 { + return func(t T.Tuple4[A1, A2, A3, A4]) GR_TUPLE4 { + return A.TraverseTuple4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + Ap[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + f1, + f2, + f3, + f4, + t, + ) + } +} + +// TraverseSeqTuple4 converts a [T.Tuple4] of readers into a reader of a [T.Tuple4]. +func TraverseSeqTuple4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) GR_TUPLE4 { + return func(t T.Tuple4[A1, A2, A3, A4]) GR_TUPLE4 { + return A.TraverseTuple4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + ApSeq[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + f1, + f2, + f3, + f4, + t, + ) + } +} + +// TraverseParTuple4 converts a [T.Tuple4] of readers into a reader of a [T.Tuple4]. +func TraverseParTuple4[ + GR_TUPLE4 ~func(context.Context) GIO_TUPLE4, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GIO_TUPLE4 ~func() E.Either[error, T.Tuple4[T1, T2, T3, T4]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) GR_TUPLE4 { + return func(t T.Tuple4[A1, A2, A3, A4]) GR_TUPLE4 { + return A.TraverseTuple4( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], func(context.Context) func() E.Either[error, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T3], + ApPar[GR_TUPLE4, func(context.Context) func() E.Either[error, func(T4) T.Tuple4[T1, T2, T3, T4]], GR_T4], + f1, + f2, + f3, + f4, + t, + ) + } +} + +// Eitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning a [GRA] +// The inverse function is [Uneitherize5] +func Eitherize5[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) GRA { + return RE.Eitherize5[GRA](f) +} + +// Uneitherize5 converts a function with 5 parameters returning a [GRA] into a function with 5 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize5[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, R any](f func(T0, T1, T2, T3, T4) GRA) F { + return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4)(c)()) + } +} + +// SequenceT5 converts 5 readers into a reader of a [T.Tuple5]. +func SequenceT5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + T1, + T2, + T3, + T4, + T5 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, +) GR_TUPLE5 { + return A.SequenceT5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + Ap[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceSeqT5 converts 5 readers into a reader of a [T.Tuple5]. +func SequenceSeqT5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + T1, + T2, + T3, + T4, + T5 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, +) GR_TUPLE5 { + return A.SequenceT5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + ApSeq[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceParT5 converts 5 readers into a reader of a [T.Tuple5]. +func SequenceParT5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + T1, + T2, + T3, + T4, + T5 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, +) GR_TUPLE5 { + return A.SequenceT5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + ApPar[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceTuple5 converts a [T.Tuple5] of readers into a reader of a [T.Tuple5]. +func SequenceTuple5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + T1, + T2, + T3, + T4, + T5 any](t T.Tuple5[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5]) GR_TUPLE5 { + return A.SequenceTuple5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + Ap[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + t, + ) +} + +// SequenceSeqTuple5 converts a [T.Tuple5] of readers into a reader of a [T.Tuple5]. +func SequenceSeqTuple5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + T1, + T2, + T3, + T4, + T5 any](t T.Tuple5[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5]) GR_TUPLE5 { + return A.SequenceTuple5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + ApSeq[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + t, + ) +} + +// SequenceParTuple5 converts a [T.Tuple5] of readers into a reader of a [T.Tuple5]. +func SequenceParTuple5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + T1, + T2, + T3, + T4, + T5 any](t T.Tuple5[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5]) GR_TUPLE5 { + return A.SequenceTuple5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + ApPar[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + t, + ) +} + +// TraverseTuple5 converts a [T.Tuple5] of readers into a reader of a [T.Tuple5]. +func TraverseTuple5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) GR_TUPLE5 { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) GR_TUPLE5 { + return A.TraverseTuple5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + Ap[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + f1, + f2, + f3, + f4, + f5, + t, + ) + } +} + +// TraverseSeqTuple5 converts a [T.Tuple5] of readers into a reader of a [T.Tuple5]. +func TraverseSeqTuple5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) GR_TUPLE5 { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) GR_TUPLE5 { + return A.TraverseTuple5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + ApSeq[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + f1, + f2, + f3, + f4, + f5, + t, + ) + } +} + +// TraverseParTuple5 converts a [T.Tuple5] of readers into a reader of a [T.Tuple5]. +func TraverseParTuple5[ + GR_TUPLE5 ~func(context.Context) GIO_TUPLE5, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GIO_TUPLE5 ~func() E.Either[error, T.Tuple5[T1, T2, T3, T4, T5]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) GR_TUPLE5 { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) GR_TUPLE5 { + return A.TraverseTuple5( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func(context.Context) func() E.Either[error, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T4], + ApPar[GR_TUPLE5, func(context.Context) func() E.Either[error, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], GR_T5], + f1, + f2, + f3, + f4, + f5, + t, + ) + } +} + +// Eitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning a [GRA] +// The inverse function is [Uneitherize6] +func Eitherize6[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) GRA { + return RE.Eitherize6[GRA](f) +} + +// Uneitherize6 converts a function with 6 parameters returning a [GRA] into a function with 6 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize6[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, R any](f func(T0, T1, T2, T3, T4, T5) GRA) F { + return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5)(c)()) + } +} + +// SequenceT6 converts 6 readers into a reader of a [T.Tuple6]. +func SequenceT6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + T1, + T2, + T3, + T4, + T5, + T6 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, +) GR_TUPLE6 { + return A.SequenceT6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + Ap[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceSeqT6 converts 6 readers into a reader of a [T.Tuple6]. +func SequenceSeqT6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + T1, + T2, + T3, + T4, + T5, + T6 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, +) GR_TUPLE6 { + return A.SequenceT6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + ApSeq[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceParT6 converts 6 readers into a reader of a [T.Tuple6]. +func SequenceParT6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + T1, + T2, + T3, + T4, + T5, + T6 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, +) GR_TUPLE6 { + return A.SequenceT6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + ApPar[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceTuple6 converts a [T.Tuple6] of readers into a reader of a [T.Tuple6]. +func SequenceTuple6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + T1, + T2, + T3, + T4, + T5, + T6 any](t T.Tuple6[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6]) GR_TUPLE6 { + return A.SequenceTuple6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + Ap[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + t, + ) +} + +// SequenceSeqTuple6 converts a [T.Tuple6] of readers into a reader of a [T.Tuple6]. +func SequenceSeqTuple6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + T1, + T2, + T3, + T4, + T5, + T6 any](t T.Tuple6[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6]) GR_TUPLE6 { + return A.SequenceTuple6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + ApSeq[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + t, + ) +} + +// SequenceParTuple6 converts a [T.Tuple6] of readers into a reader of a [T.Tuple6]. +func SequenceParTuple6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + T1, + T2, + T3, + T4, + T5, + T6 any](t T.Tuple6[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6]) GR_TUPLE6 { + return A.SequenceTuple6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + ApPar[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + t, + ) +} + +// TraverseTuple6 converts a [T.Tuple6] of readers into a reader of a [T.Tuple6]. +func TraverseTuple6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) GR_TUPLE6 { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) GR_TUPLE6 { + return A.TraverseTuple6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + Ap[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + f1, + f2, + f3, + f4, + f5, + f6, + t, + ) + } +} + +// TraverseSeqTuple6 converts a [T.Tuple6] of readers into a reader of a [T.Tuple6]. +func TraverseSeqTuple6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) GR_TUPLE6 { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) GR_TUPLE6 { + return A.TraverseTuple6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + ApSeq[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + f1, + f2, + f3, + f4, + f5, + f6, + t, + ) + } +} + +// TraverseParTuple6 converts a [T.Tuple6] of readers into a reader of a [T.Tuple6]. +func TraverseParTuple6[ + GR_TUPLE6 ~func(context.Context) GIO_TUPLE6, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GIO_TUPLE6 ~func() E.Either[error, T.Tuple6[T1, T2, T3, T4, T5, T6]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) GR_TUPLE6 { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) GR_TUPLE6 { + return A.TraverseTuple6( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func(context.Context) func() E.Either[error, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T5], + ApPar[GR_TUPLE6, func(context.Context) func() E.Either[error, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], GR_T6], + f1, + f2, + f3, + f4, + f5, + f6, + t, + ) + } +} + +// Eitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning a [GRA] +// The inverse function is [Uneitherize7] +func Eitherize7[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) GRA { + return RE.Eitherize7[GRA](f) +} + +// Uneitherize7 converts a function with 7 parameters returning a [GRA] into a function with 7 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize7[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, R any](f func(T0, T1, T2, T3, T4, T5, T6) GRA) F { + return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6)(c)()) + } +} + +// SequenceT7 converts 7 readers into a reader of a [T.Tuple7]. +func SequenceT7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + T1, + T2, + T3, + T4, + T5, + T6, + T7 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, +) GR_TUPLE7 { + return A.SequenceT7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + Ap[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceSeqT7 converts 7 readers into a reader of a [T.Tuple7]. +func SequenceSeqT7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + T1, + T2, + T3, + T4, + T5, + T6, + T7 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, +) GR_TUPLE7 { + return A.SequenceT7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + ApSeq[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceParT7 converts 7 readers into a reader of a [T.Tuple7]. +func SequenceParT7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + T1, + T2, + T3, + T4, + T5, + T6, + T7 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, +) GR_TUPLE7 { + return A.SequenceT7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + ApPar[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceTuple7 converts a [T.Tuple7] of readers into a reader of a [T.Tuple7]. +func SequenceTuple7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + T1, + T2, + T3, + T4, + T5, + T6, + T7 any](t T.Tuple7[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7]) GR_TUPLE7 { + return A.SequenceTuple7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + Ap[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + t, + ) +} + +// SequenceSeqTuple7 converts a [T.Tuple7] of readers into a reader of a [T.Tuple7]. +func SequenceSeqTuple7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + T1, + T2, + T3, + T4, + T5, + T6, + T7 any](t T.Tuple7[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7]) GR_TUPLE7 { + return A.SequenceTuple7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + ApSeq[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + t, + ) +} + +// SequenceParTuple7 converts a [T.Tuple7] of readers into a reader of a [T.Tuple7]. +func SequenceParTuple7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + T1, + T2, + T3, + T4, + T5, + T6, + T7 any](t T.Tuple7[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7]) GR_TUPLE7 { + return A.SequenceTuple7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + ApPar[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + t, + ) +} + +// TraverseTuple7 converts a [T.Tuple7] of readers into a reader of a [T.Tuple7]. +func TraverseTuple7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) GR_TUPLE7 { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) GR_TUPLE7 { + return A.TraverseTuple7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + Ap[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t, + ) + } +} + +// TraverseSeqTuple7 converts a [T.Tuple7] of readers into a reader of a [T.Tuple7]. +func TraverseSeqTuple7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) GR_TUPLE7 { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) GR_TUPLE7 { + return A.TraverseTuple7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + ApSeq[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t, + ) + } +} + +// TraverseParTuple7 converts a [T.Tuple7] of readers into a reader of a [T.Tuple7]. +func TraverseParTuple7[ + GR_TUPLE7 ~func(context.Context) GIO_TUPLE7, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GIO_TUPLE7 ~func() E.Either[error, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) GR_TUPLE7 { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) GR_TUPLE7 { + return A.TraverseTuple7( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func(context.Context) func() E.Either[error, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T6], + ApPar[GR_TUPLE7, func(context.Context) func() E.Either[error, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], GR_T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t, + ) + } +} + +// Eitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning a [GRA] +// The inverse function is [Uneitherize8] +func Eitherize8[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) GRA { + return RE.Eitherize8[GRA](f) +} + +// Uneitherize8 converts a function with 8 parameters returning a [GRA] into a function with 8 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize8[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7) GRA) F { + return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7)(c)()) + } +} + +// SequenceT8 converts 8 readers into a reader of a [T.Tuple8]. +func SequenceT8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, +) GR_TUPLE8 { + return A.SequenceT8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + Ap[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceSeqT8 converts 8 readers into a reader of a [T.Tuple8]. +func SequenceSeqT8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, +) GR_TUPLE8 { + return A.SequenceT8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + ApSeq[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceParT8 converts 8 readers into a reader of a [T.Tuple8]. +func SequenceParT8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, +) GR_TUPLE8 { + return A.SequenceT8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + ApPar[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceTuple8 converts a [T.Tuple8] of readers into a reader of a [T.Tuple8]. +func SequenceTuple8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8 any](t T.Tuple8[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8]) GR_TUPLE8 { + return A.SequenceTuple8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + Ap[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + t, + ) +} + +// SequenceSeqTuple8 converts a [T.Tuple8] of readers into a reader of a [T.Tuple8]. +func SequenceSeqTuple8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8 any](t T.Tuple8[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8]) GR_TUPLE8 { + return A.SequenceTuple8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + ApSeq[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + t, + ) +} + +// SequenceParTuple8 converts a [T.Tuple8] of readers into a reader of a [T.Tuple8]. +func SequenceParTuple8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8 any](t T.Tuple8[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8]) GR_TUPLE8 { + return A.SequenceTuple8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + ApPar[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + t, + ) +} + +// TraverseTuple8 converts a [T.Tuple8] of readers into a reader of a [T.Tuple8]. +func TraverseTuple8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) GR_TUPLE8 { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) GR_TUPLE8 { + return A.TraverseTuple8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + Ap[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t, + ) + } +} + +// TraverseSeqTuple8 converts a [T.Tuple8] of readers into a reader of a [T.Tuple8]. +func TraverseSeqTuple8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) GR_TUPLE8 { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) GR_TUPLE8 { + return A.TraverseTuple8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + ApSeq[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t, + ) + } +} + +// TraverseParTuple8 converts a [T.Tuple8] of readers into a reader of a [T.Tuple8]. +func TraverseParTuple8[ + GR_TUPLE8 ~func(context.Context) GIO_TUPLE8, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GIO_TUPLE8 ~func() E.Either[error, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) GR_TUPLE8 { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) GR_TUPLE8 { + return A.TraverseTuple8( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func(context.Context) func() E.Either[error, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T7], + ApPar[GR_TUPLE8, func(context.Context) func() E.Either[error, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], GR_T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t, + ) + } +} + +// Eitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning a [GRA] +// The inverse function is [Uneitherize9] +func Eitherize9[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) GRA { + return RE.Eitherize9[GRA](f) +} + +// Uneitherize9 converts a function with 9 parameters returning a [GRA] into a function with 9 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize9[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8) GRA) F { + return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8)(c)()) + } +} + +// SequenceT9 converts 9 readers into a reader of a [T.Tuple9]. +func SequenceT9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, + t9 GR_T9, +) GR_TUPLE9 { + return A.SequenceT9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + Ap[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + Ap[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceSeqT9 converts 9 readers into a reader of a [T.Tuple9]. +func SequenceSeqT9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, + t9 GR_T9, +) GR_TUPLE9 { + return A.SequenceT9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + ApSeq[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + ApSeq[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceParT9 converts 9 readers into a reader of a [T.Tuple9]. +func SequenceParT9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, + t9 GR_T9, +) GR_TUPLE9 { + return A.SequenceT9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + ApPar[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + ApPar[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceTuple9 converts a [T.Tuple9] of readers into a reader of a [T.Tuple9]. +func SequenceTuple9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9 any](t T.Tuple9[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8, GR_T9]) GR_TUPLE9 { + return A.SequenceTuple9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + Ap[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + Ap[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + t, + ) +} + +// SequenceSeqTuple9 converts a [T.Tuple9] of readers into a reader of a [T.Tuple9]. +func SequenceSeqTuple9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9 any](t T.Tuple9[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8, GR_T9]) GR_TUPLE9 { + return A.SequenceTuple9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + ApSeq[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + ApSeq[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + t, + ) +} + +// SequenceParTuple9 converts a [T.Tuple9] of readers into a reader of a [T.Tuple9]. +func SequenceParTuple9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9 any](t T.Tuple9[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8, GR_T9]) GR_TUPLE9 { + return A.SequenceTuple9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + ApPar[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + ApPar[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + t, + ) +} + +// TraverseTuple9 converts a [T.Tuple9] of readers into a reader of a [T.Tuple9]. +func TraverseTuple9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + F9 ~func(A9) GR_T9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8, + A9, + T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) GR_TUPLE9 { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) GR_TUPLE9 { + return A.TraverseTuple9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + Ap[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + Ap[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t, + ) + } +} + +// TraverseSeqTuple9 converts a [T.Tuple9] of readers into a reader of a [T.Tuple9]. +func TraverseSeqTuple9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + F9 ~func(A9) GR_T9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8, + A9, + T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) GR_TUPLE9 { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) GR_TUPLE9 { + return A.TraverseTuple9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + ApSeq[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + ApSeq[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t, + ) + } +} + +// TraverseParTuple9 converts a [T.Tuple9] of readers into a reader of a [T.Tuple9]. +func TraverseParTuple9[ + GR_TUPLE9 ~func(context.Context) GIO_TUPLE9, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + F9 ~func(A9) GR_T9, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GIO_TUPLE9 ~func() E.Either[error, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8, + A9, + T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) GR_TUPLE9 { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) GR_TUPLE9 { + return A.TraverseTuple9( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T7], + ApPar[func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func(context.Context) func() E.Either[error, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T8], + ApPar[GR_TUPLE9, func(context.Context) func() E.Either[error, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], GR_T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t, + ) + } +} + +// Eitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning a [GRA] +// The inverse function is [Uneitherize10] +func Eitherize10[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) GRA { + return RE.Eitherize10[GRA](f) +} + +// Uneitherize10 converts a function with 10 parameters returning a [GRA] into a function with 10 parameters returning a tuple. +// The first parameter is considered to be the [context.Context]. +func Uneitherize10[GRA ~func(context.Context) GIOA, F ~func(context.Context, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) GRA) F { + return func(c context.Context, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)(c)()) + } +} + +// SequenceT10 converts 10 readers into a reader of a [T.Tuple10]. +func SequenceT10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, + t9 GR_T9, + t10 GR_T10, +) GR_TUPLE10 { + return A.SequenceT10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + Ap[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + Ap[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + Ap[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceSeqT10 converts 10 readers into a reader of a [T.Tuple10]. +func SequenceSeqT10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, + t9 GR_T9, + t10 GR_T10, +) GR_TUPLE10 { + return A.SequenceT10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + ApSeq[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + ApSeq[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + ApSeq[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceParT10 converts 10 readers into a reader of a [T.Tuple10]. +func SequenceParT10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10 any]( + t1 GR_T1, + t2 GR_T2, + t3 GR_T3, + t4 GR_T4, + t5 GR_T5, + t6 GR_T6, + t7 GR_T7, + t8 GR_T8, + t9 GR_T9, + t10 GR_T10, +) GR_TUPLE10 { + return A.SequenceT10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + ApPar[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + ApPar[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + ApPar[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceTuple10 converts a [T.Tuple10] of readers into a reader of a [T.Tuple10]. +func SequenceTuple10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10 any](t T.Tuple10[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8, GR_T9, GR_T10]) GR_TUPLE10 { + return A.SequenceTuple10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + Ap[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + Ap[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + Ap[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + t, + ) +} + +// SequenceSeqTuple10 converts a [T.Tuple10] of readers into a reader of a [T.Tuple10]. +func SequenceSeqTuple10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10 any](t T.Tuple10[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8, GR_T9, GR_T10]) GR_TUPLE10 { + return A.SequenceTuple10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + ApSeq[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + ApSeq[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + ApSeq[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + t, + ) +} + +// SequenceParTuple10 converts a [T.Tuple10] of readers into a reader of a [T.Tuple10]. +func SequenceParTuple10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10 any](t T.Tuple10[GR_T1, GR_T2, GR_T3, GR_T4, GR_T5, GR_T6, GR_T7, GR_T8, GR_T9, GR_T10]) GR_TUPLE10 { + return A.SequenceTuple10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + ApPar[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + ApPar[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + ApPar[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + t, + ) +} + +// TraverseTuple10 converts a [T.Tuple10] of readers into a reader of a [T.Tuple10]. +func TraverseTuple10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + F9 ~func(A9) GR_T9, + F10 ~func(A10) GR_T10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8, + A9, + T9, + A10, + T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) GR_TUPLE10 { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) GR_TUPLE10 { + return A.TraverseTuple10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + Ap[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + Ap[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + Ap[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + Ap[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + Ap[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + Ap[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + Ap[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + Ap[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + Ap[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t, + ) + } +} + +// TraverseSeqTuple10 converts a [T.Tuple10] of readers into a reader of a [T.Tuple10]. +func TraverseSeqTuple10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + F9 ~func(A9) GR_T9, + F10 ~func(A10) GR_T10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8, + A9, + T9, + A10, + T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) GR_TUPLE10 { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) GR_TUPLE10 { + return A.TraverseTuple10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + ApSeq[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + ApSeq[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + ApSeq[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + ApSeq[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + ApSeq[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + ApSeq[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + ApSeq[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + ApSeq[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + ApSeq[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t, + ) + } +} + +// TraverseParTuple10 converts a [T.Tuple10] of readers into a reader of a [T.Tuple10]. +func TraverseParTuple10[ + GR_TUPLE10 ~func(context.Context) GIO_TUPLE10, + F1 ~func(A1) GR_T1, + F2 ~func(A2) GR_T2, + F3 ~func(A3) GR_T3, + F4 ~func(A4) GR_T4, + F5 ~func(A5) GR_T5, + F6 ~func(A6) GR_T6, + F7 ~func(A7) GR_T7, + F8 ~func(A8) GR_T8, + F9 ~func(A9) GR_T9, + F10 ~func(A10) GR_T10, + GR_T1 ~func(context.Context) GIO_T1, + GR_T2 ~func(context.Context) GIO_T2, + GR_T3 ~func(context.Context) GIO_T3, + GR_T4 ~func(context.Context) GIO_T4, + GR_T5 ~func(context.Context) GIO_T5, + GR_T6 ~func(context.Context) GIO_T6, + GR_T7 ~func(context.Context) GIO_T7, + GR_T8 ~func(context.Context) GIO_T8, + GR_T9 ~func(context.Context) GIO_T9, + GR_T10 ~func(context.Context) GIO_T10, + GIO_TUPLE10 ~func() E.Either[error, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + GIO_T1 ~func() E.Either[error, T1], + GIO_T2 ~func() E.Either[error, T2], + GIO_T3 ~func() E.Either[error, T3], + GIO_T4 ~func() E.Either[error, T4], + GIO_T5 ~func() E.Either[error, T5], + GIO_T6 ~func() E.Either[error, T6], + GIO_T7 ~func() E.Either[error, T7], + GIO_T8 ~func() E.Either[error, T8], + GIO_T9 ~func() E.Either[error, T9], + GIO_T10 ~func() E.Either[error, T10], + A1, + T1, + A2, + T2, + A3, + T3, + A4, + T4, + A5, + T5, + A6, + T6, + A7, + T7, + A8, + T8, + A9, + T9, + A10, + T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) GR_TUPLE10 { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) GR_TUPLE10 { + return A.TraverseTuple10( + Map[GR_T1, func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GIO_T1], + ApPar[func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T2], + ApPar[func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T3], + ApPar[func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T4], + ApPar[func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T5], + ApPar[func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T6], + ApPar[func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T7], + ApPar[func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T8], + ApPar[func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func(context.Context) func() E.Either[error, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T9], + ApPar[GR_TUPLE10, func(context.Context) func() E.Either[error, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], GR_T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t, + ) + } +} diff --git a/v2/context/readerioeither/generic/monoid.go b/v2/context/readerioeither/generic/monoid.go new file mode 100644 index 0000000..aa9bd00 --- /dev/null +++ b/v2/context/readerioeither/generic/monoid.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + ET "github.com/IBM/fp-go/v2/either" + M "github.com/IBM/fp-go/v2/monoid" +) + +func ApplicativeMonoid[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any]( + m M.Monoid[A], +) M.Monoid[GRA] { + return M.ApplicativeMonoid( + Of[GRA], + MonadMap[GRA, GRFA], + MonadAp[GRA, GRA, GRFA], + m, + ) +} + +func ApplicativeMonoidSeq[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any]( + m M.Monoid[A], +) M.Monoid[GRA] { + return M.ApplicativeMonoid( + Of[GRA], + MonadMap[GRA, GRFA], + MonadApSeq[GRA, GRA, GRFA], + m, + ) +} + +func ApplicativeMonoidPar[GRA ~func(context.Context) GIOA, GRFA ~func(context.Context) GIOFA, GIOA ~func() ET.Either[error, A], GIOFA ~func() ET.Either[error, func(A) A], A any]( + m M.Monoid[A], +) M.Monoid[GRA] { + return M.ApplicativeMonoid( + Of[GRA], + MonadMap[GRA, GRFA], + MonadApPar[GRA, GRA, GRFA], + m, + ) +} diff --git a/v2/context/readerioeither/generic/reader.go b/v2/context/readerioeither/generic/reader.go new file mode 100644 index 0000000..5e88811 --- /dev/null +++ b/v2/context/readerioeither/generic/reader.go @@ -0,0 +1,671 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + "time" + + E "github.com/IBM/fp-go/v2/either" + ER "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io/generic" + IOE "github.com/IBM/fp-go/v2/ioeither/generic" + O "github.com/IBM/fp-go/v2/option" + RIE "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +const ( + // useParallel is the feature flag to control if we use the parallel or the sequential implementation of ap + useParallel = true +) + +func FromEither[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + A any](e E.Either[error, A]) GRA { + return RIE.FromEither[GRA](e) +} + +func RightReader[ + GRA ~func(context.Context) GIOA, + GR ~func(context.Context) A, + GIOA ~func() E.Either[error, A], + A any](r GR) GRA { + return RIE.RightReader[GR, GRA](r) +} + +func LeftReader[ + GRA ~func(context.Context) GIOA, + GR ~func(context.Context) error, + GIOA ~func() E.Either[error, A], + A any](l GR) GRA { + return RIE.LeftReader[GR, GRA](l) +} + +func Left[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + A any](l error) GRA { + return RIE.Left[GRA](l) +} + +func Right[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + A any](r A) GRA { + return RIE.Right[GRA](r) +} + +func FromReader[ + GRA ~func(context.Context) GIOA, + GR ~func(context.Context) A, + GIOA ~func() E.Either[error, A], + A any](r GR) GRA { + return RIE.FromReader[GR, GRA](r) +} + +func MonadMap[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + A, B any](fa GRA, f func(A) B) GRB { + return RIE.MonadMap[GRA, GRB](fa, f) +} + +func Map[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + A, B any](f func(A) B) func(GRA) GRB { + return RIE.Map[GRA, GRB](f) +} + +func MonadMapTo[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + A, B any](fa GRA, b B) GRB { + return RIE.MonadMapTo[GRA, GRB](fa, b) +} + +func MapTo[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + A, B any](b B) func(GRA) GRB { + return RIE.MapTo[GRA, GRB](b) +} + +func MonadChain[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + A, B any](ma GRA, f func(A) GRB) GRB { + return RIE.MonadChain(ma, f) +} + +func Chain[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + A, B any](f func(A) GRB) func(GRA) GRB { + return RIE.Chain[GRA](f) +} + +func MonadChainFirst[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + A, B any](ma GRA, f func(A) GRB) GRA { + return RIE.MonadChainFirst(ma, f) +} + +func ChainFirst[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + A, B any](f func(A) GRB) func(GRA) GRA { + return RIE.ChainFirst[GRA](f) +} + +func Of[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A any](a A) GRA { + return RIE.Of[GRA](a) +} + +// withCancelCauseFunc wraps an IOEither such that in case of an error the cancel function is invoked +func withCancelCauseFunc[ + GIOA ~func() E.Either[error, A], + A any](cancel context.CancelCauseFunc, ma GIOA) GIOA { + return F.Pipe3( + ma, + IOE.Swap[GIOA, func() E.Either[A, error]], + IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any { + return IO.FromImpure[func() any](func() { cancel(err) }) + }), + IOE.Swap[func() E.Either[A, error], GIOA], + ) +} + +// MonadApSeq implements the `Ap` function for a reader with context. It creates a sub-context that will +// be canceled if any of the input operations errors out or +func MonadApSeq[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + GRAB ~func(context.Context) GIOAB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + GIOAB ~func() E.Either[error, func(A) B], + + A, B any](fab GRAB, fa GRA) GRB { + + return RIE.MonadApSeq[GRA, GRB](fab, fa) +} + +// MonadAp implements the `Ap` function for a reader with context. It creates a sub-context that will +// be canceled if any of the input operations errors out or +func MonadApPar[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + GRAB ~func(context.Context) GIOAB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + GIOAB ~func() E.Either[error, func(A) B], + + A, B any](fab GRAB, fa GRA) GRB { + // context sensitive input + cfab := WithContext(fab) + cfa := WithContext(fa) + + return func(ctx context.Context) GIOB { + // quick check for cancellation + if err := context.Cause(ctx); err != nil { + return IOE.Left[GIOB](err) + } + + return func() E.Either[error, B] { + // quick check for cancellation + if err := context.Cause(ctx); err != nil { + return E.Left[B](err) + } + + // create sub-contexts for fa and fab, so they can cancel one other + ctxSub, cancelSub := context.WithCancelCause(ctx) + defer cancelSub(nil) // cancel has to be called in all paths + + fabIOE := withCancelCauseFunc(cancelSub, cfab(ctxSub)) + faIOE := withCancelCauseFunc(cancelSub, cfa(ctxSub)) + + return IOE.MonadApPar[GIOB, GIOAB](fabIOE, faIOE)() + } + } +} + +// MonadAp implements the `Ap` function for a reader with context. It creates a sub-context that will +// be canceled if any of the input operations errors out or +func MonadAp[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + GRAB ~func(context.Context) GIOAB, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + GIOAB ~func() E.Either[error, func(A) B], + + A, B any](fab GRAB, fa GRA) GRB { + // dispatch to the configured version + if useParallel { + return MonadApPar[GRB](fab, fa) + } + return MonadApSeq[GRB](fab, fa) +} + +func Ap[ + GRB ~func(context.Context) GIOB, + GRAB ~func(context.Context) GIOAB, + GRA ~func(context.Context) GIOA, + + GIOB ~func() E.Either[error, B], + GIOAB ~func() E.Either[error, func(A) B], + GIOA ~func() E.Either[error, A], + + A, B any](fa GRA) func(GRAB) GRB { + return F.Bind2nd(MonadAp[GRB, GRA, GRAB], fa) +} + +func ApSeq[ + GRB ~func(context.Context) GIOB, + GRAB ~func(context.Context) GIOAB, + GRA ~func(context.Context) GIOA, + + GIOB ~func() E.Either[error, B], + GIOAB ~func() E.Either[error, func(A) B], + GIOA ~func() E.Either[error, A], + + A, B any](fa GRA) func(GRAB) GRB { + return F.Bind2nd(MonadApSeq[GRB, GRA, GRAB], fa) +} + +func ApPar[ + GRB ~func(context.Context) GIOB, + GRAB ~func(context.Context) GIOAB, + GRA ~func(context.Context) GIOA, + + GIOB ~func() E.Either[error, B], + GIOAB ~func() E.Either[error, func(A) B], + GIOA ~func() E.Either[error, A], + + A, B any](fa GRA) func(GRAB) GRB { + return F.Bind2nd(MonadApPar[GRB, GRA, GRAB], fa) +} + +func FromPredicate[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A any](pred func(A) bool, onFalse func(A) error) func(A) GRA { + return RIE.FromPredicate[GRA](pred, onFalse) +} + +func Fold[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + + GIOA ~func() E.Either[error, A], + GIOB ~func() B, + + A, B any](onLeft func(error) GRB, onRight func(A) GRB) func(GRA) GRB { + return RIE.Fold[GRB, GRA](onLeft, onRight) +} + +func GetOrElse[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + GIOB ~func() A, + + A any](onLeft func(error) GRB) func(GRA) GRB { + return RIE.GetOrElse[GRB, GRA](onLeft) +} + +func OrElse[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A any](onLeft func(error) GRA) func(GRA) GRA { + return RIE.OrElse[GRA](onLeft) +} + +func OrLeft[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + GIOA ~func() E.Either[error, A], + + GIOB ~func() error, + + A any](onLeft func(error) GRB) func(GRA) GRA { + return RIE.OrLeft[GRA, GRB, GRA](onLeft) +} + +func Ask[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, context.Context], + +]() GRA { + return RIE.Ask[GRA]() +} + +func Asks[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) A, + GIOA ~func() E.Either[error, A], + + A any](r GRB) GRA { + + return RIE.Asks[GRB, GRA](r) +} + +func MonadChainEitherK[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + GIOA ~func() E.Either[error, A], + + GIOB ~func() E.Either[error, B], + + A, B any](ma GRA, f func(A) E.Either[error, B]) GRB { + return RIE.MonadChainEitherK[GRA, GRB](ma, f) +} + +func ChainEitherK[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + GIOA ~func() E.Either[error, A], + + GIOB ~func() E.Either[error, B], + + A, B any](f func(A) E.Either[error, B]) func(ma GRA) GRB { + return RIE.ChainEitherK[GRA, GRB](f) +} + +func MonadChainFirstEitherK[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A, B any](ma GRA, f func(A) E.Either[error, B]) GRA { + return RIE.MonadChainFirstEitherK[GRA](ma, f) +} + +func ChainFirstEitherK[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A, B any](f func(A) E.Either[error, B]) func(ma GRA) GRA { + return RIE.ChainFirstEitherK[GRA](f) +} + +func ChainOptionK[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + + GIOB ~func() E.Either[error, B], + + GIOA ~func() E.Either[error, A], + + A, B any](onNone func() error) func(func(A) O.Option[B]) func(GRA) GRB { + return RIE.ChainOptionK[GRA, GRB](onNone) +} + +func FromIOEither[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A any](t GIOA) GRA { + return RIE.FromIOEither[GRA](t) +} + +func FromIO[ + GRA ~func(context.Context) GIOA, + GIOB ~func() A, + + GIOA ~func() E.Either[error, A], + + A any](t GIOB) GRA { + return RIE.FromIO[GRA](t) +} + +// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled +func Never[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A any]() GRA { + return func(ctx context.Context) GIOA { + return IOE.MakeIO(func() E.Either[error, A] { + <-ctx.Done() + return E.Left[A](context.Cause(ctx)) + }) + } +} + +func MonadChainIOK[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + GIOB ~func() E.Either[error, B], + + GIO ~func() B, + + A, B any](ma GRA, f func(A) GIO) GRB { + return RIE.MonadChainIOK[GRA, GRB](ma, f) +} + +func ChainIOK[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + GIOB ~func() E.Either[error, B], + + GIO ~func() B, + + A, B any](f func(A) GIO) func(ma GRA) GRB { + return RIE.ChainIOK[GRA, GRB](f) +} + +func MonadChainReaderIOK[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + GRIO ~func(context.Context) GIO, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + GIO ~func() B, + + A, B any](ma GRA, f func(A) GRIO) GRB { + return RIE.MonadChainReaderIOK[GRA, GRB](ma, f) +} + +func ChainReaderIOK[ + GRB ~func(context.Context) GIOB, + GRA ~func(context.Context) GIOA, + GRIO ~func(context.Context) GIO, + + GIOA ~func() E.Either[error, A], + GIOB ~func() E.Either[error, B], + + GIO ~func() B, + + A, B any](f func(A) GRIO) func(ma GRA) GRB { + return RIE.ChainReaderIOK[GRA, GRB](f) +} + +func MonadChainFirstIOK[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + GIO ~func() B, + + A, B any](ma GRA, f func(A) GIO) GRA { + return RIE.MonadChainFirstIOK[GRA](ma, f) +} + +func ChainFirstIOK[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + GIO ~func() B, + + A, B any](f func(A) GIO) func(ma GRA) GRA { + return RIE.ChainFirstIOK[GRA](f) +} + +func ChainIOEitherK[ + GRA ~func(context.Context) GIOA, + GRB ~func(context.Context) GIOB, + GIOA ~func() E.Either[error, A], + + GIOB ~func() E.Either[error, B], + + A, B any](f func(A) GIOB) func(ma GRA) GRB { + return RIE.ChainIOEitherK[GRA, GRB](f) +} + +// Delay creates an operation that passes in the value after some delay +func Delay[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A any](delay time.Duration) func(ma GRA) GRA { + return func(ma GRA) GRA { + return func(ctx context.Context) GIOA { + return IOE.MakeIO(func() E.Either[error, A] { + // manage the timeout + timeoutCtx, cancelTimeout := context.WithTimeout(ctx, delay) + defer cancelTimeout() + // whatever comes first + select { + case <-timeoutCtx.Done(): + return ma(ctx)() + case <-ctx.Done(): + return E.Left[A](context.Cause(ctx)) + } + }) + } + } +} + +// Timer will return the current time after an initial delay +func Timer[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, time.Time], + +](delay time.Duration) GRA { + return F.Pipe2( + IO.Now[func() time.Time](), + FromIO[GRA, func() time.Time], + Delay[GRA](delay), + ) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A any](gen func() GRA) GRA { + return RIE.Defer[GRA](gen) +} + +// TryCatch wraps a reader returning a tuple as an error into ReaderIOEither +func TryCatch[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + + A any](f func(context.Context) func() (A, error)) GRA { + return RIE.TryCatch[GRA](f, ER.IdentityError) +} + +func MonadAlt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](first GEA, second LAZY) GEA { + return RIE.MonadAlt(first, second) +} + +func Alt[LAZY ~func() GEA, GEA ~func(context.Context) GIOA, GIOA ~func() E.Either[error, A], A any](second LAZY) func(GEA) GEA { + return RIE.Alt(second) +} + +// Memoize computes the value of the provided monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + A any](rdr GRA) GRA { + return RIE.Memoize[GRA](rdr) +} + +func Flatten[ + GGRA ~func(context.Context) GGIOA, + GGIOA ~func() E.Either[error, GRA], + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + A any](rdr GGRA) GRA { + return RIE.Flatten[GRA](rdr) +} + +func MonadFromReaderIO[ + GRIOEA ~func(context.Context) GIOEA, + GIOEA ~func() E.Either[error, A], + + GRIOA ~func(context.Context) GIOA, + GIOA ~func() A, + + A any](a A, f func(A) GRIOA) GRIOEA { + return RIE.MonadFromReaderIO[GRIOEA](a, f) +} + +func FromReaderIO[ + GRIOEA ~func(context.Context) GIOEA, + GIOEA ~func() E.Either[error, A], + + GRIOA ~func(context.Context) GIOA, + GIOA ~func() A, + + A any](f func(A) GRIOA) func(A) GRIOEA { + return RIE.FromReaderIO[GRIOEA](f) +} + +func RightReaderIO[ + GRIOEA ~func(context.Context) GIOEA, + GIOEA ~func() E.Either[error, A], + + GRIOA ~func(context.Context) GIOA, + GIOA ~func() A, + + A any](ma GRIOA) GRIOEA { + return RIE.RightReaderIO[GRIOEA](ma) +} + +func LeftReaderIO[ + GRIOEA ~func(context.Context) GIOEA, + GIOEA ~func() E.Either[error, A], + + GRIOE ~func(context.Context) GIOE, + GIOE ~func() error, + + A any](ma GRIOE) GRIOEA { + return RIE.LeftReaderIO[GRIOEA](ma) +} + +func MonadFlap[GREAB ~func(context.Context) GEAB, GREB ~func(context.Context) GEB, GEAB ~func() E.Either[error, func(A) B], GEB ~func() E.Either[error, B], B, A any](fab GREAB, a A) GREB { + return RIE.MonadFlap[GREAB, GREB](fab, a) +} + +func Flap[GREAB ~func(context.Context) GEAB, GREB ~func(context.Context) GEB, GEAB ~func() E.Either[error, func(A) B], GEB ~func() E.Either[error, B], B, A any](a A) func(GREAB) GREB { + return RIE.Flap[GREAB, GREB](a) +} diff --git a/v2/context/readerioeither/generic/resource.go b/v2/context/readerioeither/generic/resource.go new file mode 100644 index 0000000..de34b67 --- /dev/null +++ b/v2/context/readerioeither/generic/resource.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + RIE "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[ + GRA ~func(context.Context) GIOA, + GRR ~func(context.Context) GIOR, + GRANY ~func(context.Context) GIOANY, + GIOR ~func() E.Either[error, R], + GIOA ~func() E.Either[error, A], + GIOANY ~func() E.Either[error, ANY], + R, A, ANY any](onCreate GRR, onRelease func(R) GRANY) func(func(R) GRA) GRA { + // wraps the callback functions with a context check + return F.Flow2( + F.Bind2nd(F.Flow2[func(R) GRA, func(GRA) GRA, R, GRA, GRA], WithContext[GRA]), + RIE.WithResource[GRA](WithContext(onCreate), onRelease), + ) +} diff --git a/v2/context/readerioeither/generic/semigroup.go b/v2/context/readerioeither/generic/semigroup.go new file mode 100644 index 0000000..5c26ffc --- /dev/null +++ b/v2/context/readerioeither/generic/semigroup.go @@ -0,0 +1,29 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + ET "github.com/IBM/fp-go/v2/either" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func AltSemigroup[GRA ~func(context.Context) GIOA, GIOA ~func() ET.Either[error, A], A any]() S.Semigroup[GRA] { + return S.AltSemigroup( + MonadAlt[func() GRA], + ) +} diff --git a/v2/context/readerioeither/generic/sync.go b/v2/context/readerioeither/generic/sync.go new file mode 100644 index 0000000..5c75703 --- /dev/null +++ b/v2/context/readerioeither/generic/sync.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[ + GRA ~func(context.Context) GIOA, + GIOA ~func() E.Either[error, A], + GRCANCEL ~func(context.Context) GIOCANCEL, + GIOCANCEL ~func() E.Either[error, context.CancelFunc], + A any](lock GRCANCEL) func(fa GRA) GRA { + + type GRANY func(ctx context.Context) func() E.Either[error, any] + type IOANY func() any + + return F.Flow2( + F.Constant1[context.CancelFunc, GRA], + WithResource[GRA, GRCANCEL, GRANY](lock, F.Flow2( + IO.FromImpure[IOANY, context.CancelFunc], + FromIO[GRANY, IOANY], + )), + ) +} diff --git a/v2/context/readerioeither/generic/traverse.go b/v2/context/readerioeither/generic/traverse.go new file mode 100644 index 0000000..7ac5a7d --- /dev/null +++ b/v2/context/readerioeither/generic/traverse.go @@ -0,0 +1,454 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" + RR "github.com/IBM/fp-go/v2/internal/record" +) + +// MonadTraverseArray transforms an array +func MonadTraverseArray[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](as AS, f func(A) GRB) GRBS { + + return RA.MonadTraverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + as, f, + ) +} + +// TraverseArray transforms an array +func TraverseArray[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](f func(A) GRB) func(AS) GRBS { + + return RA.Traverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](f func(int, A) GRB) func(AS) GRBS { + + return RA.TraverseWithIndex[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[ + AS ~[]A, + GAS ~[]GRA, + GRAS ~func(context.Context) GIOAS, + GRA ~func(context.Context) GIOA, + GIOAS ~func() E.Either[error, AS], + GIOA ~func() E.Either[error, A], + A any](ma GAS) GRAS { + + return MonadTraverseArray[GAS, GRAS](ma, F.Identity[GRA]) +} + +// MonadTraverseRecord transforms a record +func MonadTraverseRecord[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](ma AS, f func(A) GRB) GRBS { + + return RR.MonadTraverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + ma, f, + ) +} + +// TraverseRecord transforms a record +func TraverseRecord[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](f func(A) GRB) func(AS) GRBS { + + return RR.Traverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// TraverseRecordWithIndex transforms a record +func TraverseRecordWithIndex[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](f func(K, A) GRB) func(AS) GRBS { + + return RR.TraverseWithIndex[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + Ap[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// SequenceRecord converts a homogeneous sequence of either into an either of sequence +func SequenceRecord[K comparable, + AS ~map[K]A, + GAS ~map[K]GRA, + GRAS ~func(context.Context) GIOAS, + GRA ~func(context.Context) GIOA, + GIOAS ~func() E.Either[error, AS], + GIOA ~func() E.Either[error, A], + A any](ma GAS) GRAS { + + return MonadTraverseRecord[K, GAS, GRAS](ma, F.Identity[GRA]) +} + +// MonadTraverseArraySeq transforms an array +func MonadTraverseArraySeq[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](as AS, f func(A) GRB) GRBS { + + return RA.MonadTraverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + as, f, + ) +} + +// TraverseArraySeq transforms an array +func TraverseArraySeq[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](f func(A) GRB) func(AS) GRBS { + + return RA.Traverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// TraverseArrayWithIndexSeq transforms an array +func TraverseArrayWithIndexSeq[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](f func(int, A) GRB) func(AS) GRBS { + + return RA.TraverseWithIndex[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence +func SequenceArraySeq[ + AS ~[]A, + GAS ~[]GRA, + GRAS ~func(context.Context) GIOAS, + GRA ~func(context.Context) GIOA, + GIOAS ~func() E.Either[error, AS], + GIOA ~func() E.Either[error, A], + A any](ma GAS) GRAS { + + return MonadTraverseArraySeq[GAS, GRAS](ma, F.Identity[GRA]) +} + +// MonadTraverseRecordSeq transforms a record +func MonadTraverseRecordSeq[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](ma AS, f func(A) GRB) GRBS { + + return RR.MonadTraverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + ma, f, + ) +} + +// TraverseRecordSeq transforms a record +func TraverseRecordSeq[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](f func(A) GRB) func(AS) GRBS { + + return RR.Traverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// TraverseRecordWithIndexSeq transforms a record +func TraverseRecordWithIndexSeq[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](f func(K, A) GRB) func(AS) GRBS { + + return RR.TraverseWithIndex[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApSeq[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence +func SequenceRecordSeq[K comparable, + AS ~map[K]A, + GAS ~map[K]GRA, + GRAS ~func(context.Context) GIOAS, + GRA ~func(context.Context) GIOA, + GIOAS ~func() E.Either[error, AS], + GIOA ~func() E.Either[error, A], + A any](ma GAS) GRAS { + + return MonadTraverseRecordSeq[K, GAS, GRAS](ma, F.Identity[GRA]) +} + +// MonadTraverseArrayPar transforms an array +func MonadTraverseArrayPar[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](as AS, f func(A) GRB) GRBS { + + return RA.MonadTraverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + as, f, + ) +} + +// TraverseArrayPar transforms an array +func TraverseArrayPar[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](f func(A) GRB) func(AS) GRBS { + + return RA.Traverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// TraverseArrayWithIndexPar transforms an array +func TraverseArrayWithIndexPar[ + AS ~[]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~[]B, + A, B any](f func(int, A) GRB) func(AS) GRBS { + + return RA.TraverseWithIndex[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence +func SequenceArrayPar[ + AS ~[]A, + GAS ~[]GRA, + GRAS ~func(context.Context) GIOAS, + GRA ~func(context.Context) GIOA, + GIOAS ~func() E.Either[error, AS], + GIOA ~func() E.Either[error, A], + A any](ma GAS) GRAS { + + return MonadTraverseArrayPar[GAS, GRAS](ma, F.Identity[GRA]) +} + +// MonadTraverseRecordPar transforms a record +func MonadTraverseRecordPar[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](ma AS, f func(A) GRB) GRBS { + + return RR.MonadTraverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + ma, f, + ) +} + +// TraverseRecordPar transforms a record +func TraverseRecordPar[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](f func(A) GRB) func(AS) GRBS { + + return RR.Traverse[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// TraverseRecordWithIndexPar transforms a record +func TraverseRecordWithIndexPar[K comparable, + AS ~map[K]A, + GRBS ~func(context.Context) GIOBS, + GRB ~func(context.Context) GIOB, + GIOBS ~func() E.Either[error, BS], + GIOB ~func() E.Either[error, B], + BS ~map[K]B, + + A, B any](f func(K, A) GRB) func(AS) GRBS { + + return RR.TraverseWithIndex[AS]( + Of[GRBS, GIOBS, BS], + Map[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GIOBS, func() E.Either[error, func(B) BS], BS, func(B) BS], + ApPar[GRBS, func(context.Context) func() E.Either[error, func(B) BS], GRB], + + f, + ) +} + +// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence +func SequenceRecordPar[K comparable, + AS ~map[K]A, + GAS ~map[K]GRA, + GRAS ~func(context.Context) GIOAS, + GRA ~func(context.Context) GIOA, + GIOAS ~func() E.Either[error, AS], + GIOA ~func() E.Either[error, A], + A any](ma GAS) GRAS { + + return MonadTraverseRecordPar[K, GAS, GRAS](ma, F.Identity[GRA]) +} diff --git a/v2/context/readerioeither/http/builder/builder.go b/v2/context/readerioeither/http/builder/builder.go new file mode 100644 index 0000000..aaefa3f --- /dev/null +++ b/v2/context/readerioeither/http/builder/builder.go @@ -0,0 +1,72 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package builder + +import ( + "bytes" + "context" + "net/http" + "strconv" + + RIOE "github.com/IBM/fp-go/v2/context/readerioeither" + RIOEH "github.com/IBM/fp-go/v2/context/readerioeither/http" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + R "github.com/IBM/fp-go/v2/http/builder" + H "github.com/IBM/fp-go/v2/http/headers" + LZ "github.com/IBM/fp-go/v2/lazy" + O "github.com/IBM/fp-go/v2/option" +) + +func Requester(builder *R.Builder) RIOEH.Requester { + + withBody := F.Curry3(func(data []byte, url string, method string) RIOE.ReaderIOEither[*http.Request] { + return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) { + return func() (*http.Request, error) { + req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(data)) + if err == nil { + req.Header.Set(H.ContentLength, strconv.Itoa(len(data))) + H.Monoid.Concat(req.Header, builder.GetHeaders()) + } + return req, err + } + }) + }) + + withoutBody := F.Curry2(func(url string, method string) RIOE.ReaderIOEither[*http.Request] { + return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) { + return func() (*http.Request, error) { + req, err := http.NewRequestWithContext(ctx, method, url, nil) + if err == nil { + H.Monoid.Concat(req.Header, builder.GetHeaders()) + } + return req, err + } + }) + }) + + return F.Pipe5( + builder.GetBody(), + O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)), + E.Ap[func(string) RIOE.ReaderIOEither[*http.Request]](builder.GetTargetURL()), + E.Flap[error, RIOE.ReaderIOEither[*http.Request]](builder.GetMethod()), + E.GetOrElse(RIOE.Left[*http.Request]), + RIOE.Map(func(req *http.Request) *http.Request { + req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders()) + return req + }), + ) +} diff --git a/v2/context/readerioeither/http/builder/builder_test.go b/v2/context/readerioeither/http/builder/builder_test.go new file mode 100644 index 0000000..88ceca5 --- /dev/null +++ b/v2/context/readerioeither/http/builder/builder_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package builder + +import ( + "context" + "net/http" + "net/url" + "testing" + + RIOE "github.com/IBM/fp-go/v2/context/readerioeither" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + R "github.com/IBM/fp-go/v2/http/builder" + IO "github.com/IBM/fp-go/v2/io" + "github.com/stretchr/testify/assert" +) + +func TestBuilderWithQuery(t *testing.T) { + // add some query + withLimit := R.WithQueryArg("limit")("10") + withURL := R.WithURL("http://www.example.org?a=b") + + b := F.Pipe2( + R.Default, + withLimit, + withURL, + ) + + req := F.Pipe3( + b, + Requester, + RIOE.Map(func(r *http.Request) *url.URL { + return r.URL + }), + RIOE.ChainFirstIOK(func(u *url.URL) IO.IO[any] { + return IO.FromImpure(func() { + q := u.Query() + assert.Equal(t, "10", q.Get("limit")) + assert.Equal(t, "b", q.Get("a")) + }) + }), + ) + + assert.True(t, E.IsRight(req(context.Background())())) +} diff --git a/v2/context/readerioeither/http/request.go b/v2/context/readerioeither/http/request.go new file mode 100644 index 0000000..e15a8dd --- /dev/null +++ b/v2/context/readerioeither/http/request.go @@ -0,0 +1,129 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "io" + "net/http" + + B "github.com/IBM/fp-go/v2/bytes" + RIOE "github.com/IBM/fp-go/v2/context/readerioeither" + F "github.com/IBM/fp-go/v2/function" + H "github.com/IBM/fp-go/v2/http" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOEF "github.com/IBM/fp-go/v2/ioeither/file" + J "github.com/IBM/fp-go/v2/json" + P "github.com/IBM/fp-go/v2/pair" +) + +type ( + // Requester is a reader that constructs a request + Requester = RIOE.ReaderIOEither[*http.Request] + + Client interface { + // Do can send an HTTP request considering a context + Do(Requester) RIOE.ReaderIOEither[*http.Response] + } + + client struct { + delegate *http.Client + doIOE func(*http.Request) IOE.IOEither[error, *http.Response] + } +) + +var ( + // MakeRequest is an eitherized version of [http.NewRequestWithContext] + MakeRequest = RIOE.Eitherize3(http.NewRequestWithContext) + makeRequest = F.Bind13of3(MakeRequest) + + // specialize + MakeGetRequest = makeRequest("GET", nil) +) + +func (client client) Do(req Requester) RIOE.ReaderIOEither[*http.Response] { + return F.Pipe1( + req, + RIOE.ChainIOEitherK(client.doIOE), + ) +} + +// MakeClient creates an HTTP client proxy +func MakeClient(httpClient *http.Client) Client { + return client{delegate: httpClient, doIOE: IOE.Eitherize1(httpClient.Do)} +} + +// ReadFullResponse sends a request, reads the response as a byte array and represents the result as a tuple +func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullResponse] { + return func(req Requester) RIOE.ReaderIOEither[H.FullResponse] { + return F.Flow3( + client.Do(req), + IOE.ChainEitherK(H.ValidateResponse), + IOE.Chain(func(resp *http.Response) IOE.IOEither[error, H.FullResponse] { + return F.Pipe1( + F.Pipe3( + resp, + H.GetBody, + IOE.Of[error, io.ReadCloser], + IOEF.ReadAll[io.ReadCloser], + ), + IOE.Map[error](F.Bind1st(P.MakePair[*http.Response, []byte], resp)), + ) + }), + ) + } +} + +// ReadAll sends a request and reads the response as bytes +func ReadAll(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] { + return F.Flow2( + ReadFullResponse(client), + RIOE.Map(H.Body), + ) +} + +// ReadText sends a request, reads the response and represents the response as a text string +func ReadText(client Client) func(Requester) RIOE.ReaderIOEither[string] { + return F.Flow2( + ReadAll(client), + RIOE.Map(B.ToString), + ) +} + +// ReadJson sends a request, reads the response and parses the response as JSON +// +// Deprecated: use [ReadJSON] instead +func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] { + return ReadJSON[A](client) +} + +func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] { + return F.Flow3( + ReadFullResponse(client), + RIOE.ChainFirstEitherK(F.Flow2( + H.Response, + H.ValidateJSONResponse, + )), + RIOE.Map(H.Body), + ) +} + +// ReadJSON sends a request, reads the response and parses the response as JSON +func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] { + return F.Flow2( + readJSON(client), + RIOE.ChainEitherK(J.Unmarshal[A]), + ) +} diff --git a/v2/context/readerioeither/http/request_test.go b/v2/context/readerioeither/http/request_test.go new file mode 100644 index 0000000..437fcb4 --- /dev/null +++ b/v2/context/readerioeither/http/request_test.go @@ -0,0 +1,157 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "context" + "fmt" + "testing" + + H "net/http" + + R "github.com/IBM/fp-go/v2/context/readerioeither" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + "github.com/stretchr/testify/assert" +) + +type PostItem struct { + UserID uint `json:"userId"` + Id uint `json:"id"` + Title string `json:"title"` + Body string `json:"body"` +} + +func getTitle(item PostItem) string { + return item.Title +} + +type simpleRequestBuilder struct { + method string + url string + headers H.Header +} + +func requestBuilder() simpleRequestBuilder { + return simpleRequestBuilder{method: "GET"} +} + +func (b simpleRequestBuilder) WithURL(url string) simpleRequestBuilder { + b.url = url + return b +} + +func (b simpleRequestBuilder) WithHeader(key, value string) simpleRequestBuilder { + if b.headers == nil { + b.headers = make(H.Header) + } else { + b.headers = b.headers.Clone() + } + b.headers.Set(key, value) + return b +} + +func (b simpleRequestBuilder) Build() R.ReaderIOEither[*H.Request] { + return func(ctx context.Context) IOE.IOEither[error, *H.Request] { + return IOE.TryCatchError(func() (*H.Request, error) { + req, err := H.NewRequestWithContext(ctx, b.method, b.url, nil) + if err == nil { + req.Header = b.headers + } + return req, err + }) + } +} + +func TestSendSingleRequest(t *testing.T) { + + client := MakeClient(H.DefaultClient) + + req1 := MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1") + + readItem := ReadJSON[PostItem](client) + + resp1 := readItem(req1) + + resE := resp1(context.TODO())() + + fmt.Println(resE) +} + +// setHeaderUnsafe updates a header value in a request object by mutating the request object +func setHeaderUnsafe(key, value string) func(*H.Request) *H.Request { + return func(req *H.Request) *H.Request { + req.Header.Set(key, value) + return req + } +} + +func TestSendSingleRequestWithHeaderUnsafe(t *testing.T) { + + client := MakeClient(H.DefaultClient) + + // this is not safe from a puristic perspective, because the map call mutates the request object + req1 := F.Pipe2( + "https://jsonplaceholder.typicode.com/posts/1", + MakeGetRequest, + R.Map(setHeaderUnsafe("Content-Type", "text/html")), + ) + + readItem := ReadJSON[PostItem](client) + + resp1 := F.Pipe2( + req1, + readItem, + R.Map(getTitle), + ) + + res := F.Pipe1( + resp1(context.TODO())(), + E.GetOrElse(errors.ToString), + ) + + assert.Equal(t, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", res) +} + +func TestSendSingleRequestWithHeaderSafe(t *testing.T) { + + client := MakeClient(H.DefaultClient) + + // the request builder assembles config values to construct + // the final http request. Each `With` step creates a copy of the settings + // so the flow is pure + request := requestBuilder(). + WithURL("https://jsonplaceholder.typicode.com/posts/1"). + WithHeader("Content-Type", "text/html"). + Build() + + readItem := ReadJSON[PostItem](client) + + response := F.Pipe2( + request, + readItem, + R.Map(getTitle), + ) + + res := F.Pipe1( + response(context.TODO())(), + E.GetOrElse(errors.ToString), + ) + + assert.Equal(t, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", res) +} diff --git a/v2/context/readerioeither/monoid.go b/v2/context/readerioeither/monoid.go new file mode 100644 index 0000000..508fbe7 --- /dev/null +++ b/v2/context/readerioeither/monoid.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" + L "github.com/IBM/fp-go/v2/lazy" + M "github.com/IBM/fp-go/v2/monoid" +) + +// ApplicativeMonoid returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative +func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] { + return G.ApplicativeMonoid[ReaderIOEither[A], ReaderIOEither[func(A) A]](m) +} + +// ApplicativeMonoidSeq returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative +func ApplicativeMonoidSeq[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] { + return G.ApplicativeMonoidSeq[ReaderIOEither[A], ReaderIOEither[func(A) A]](m) +} + +// ApplicativeMonoidPar returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative +func ApplicativeMonoidPar[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] { + return G.ApplicativeMonoidPar[ReaderIOEither[A], ReaderIOEither[func(A) A]](m) +} + +// AlternativeMonoid is the alternative [Monoid] for [ReaderIOEither] +func AlternativeMonoid[A any](m M.Monoid[A]) M.Monoid[ReaderIOEither[A]] { + return M.AlternativeMonoid( + Of[A], + MonadMap[A, func(A) A], + MonadAp[A, A], + MonadAlt[A], + m, + ) +} + +// AltMonoid is the alternative [Monoid] for an [ReaderIOEither] +func AltMonoid[A any](zero L.Lazy[ReaderIOEither[A]]) M.Monoid[ReaderIOEither[A]] { + return M.AltMonoid( + zero, + MonadAlt[A], + ) +} diff --git a/v2/context/readerioeither/reader.go b/v2/context/readerioeither/reader.go new file mode 100644 index 0000000..5893f7b --- /dev/null +++ b/v2/context/readerioeither/reader.go @@ -0,0 +1,218 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + "time" + + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" + ET "github.com/IBM/fp-go/v2/either" + IO "github.com/IBM/fp-go/v2/io" + IOE "github.com/IBM/fp-go/v2/ioeither" + L "github.com/IBM/fp-go/v2/lazy" + O "github.com/IBM/fp-go/v2/option" + RIO "github.com/IBM/fp-go/v2/readerio" +) + +func FromEither[A any](e ET.Either[error, A]) ReaderIOEither[A] { + return G.FromEither[ReaderIOEither[A]](e) +} + +func Left[A any](l error) ReaderIOEither[A] { + return G.Left[ReaderIOEither[A]](l) +} + +func Right[A any](r A) ReaderIOEither[A] { + return G.Right[ReaderIOEither[A]](r) +} + +func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] { + return G.MonadMap[ReaderIOEither[A], ReaderIOEither[B]](fa, f) +} + +func Map[A, B any](f func(A) B) func(ReaderIOEither[A]) ReaderIOEither[B] { + return G.Map[ReaderIOEither[A], ReaderIOEither[B]](f) +} + +func MonadMapTo[A, B any](fa ReaderIOEither[A], b B) ReaderIOEither[B] { + return G.MonadMapTo[ReaderIOEither[A], ReaderIOEither[B]](fa, b) +} + +func MapTo[A, B any](b B) func(ReaderIOEither[A]) ReaderIOEither[B] { + return G.MapTo[ReaderIOEither[A], ReaderIOEither[B]](b) +} + +func MonadChain[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[B] { + return G.MonadChain(ma, f) +} + +func Chain[A, B any](f func(A) ReaderIOEither[B]) func(ReaderIOEither[A]) ReaderIOEither[B] { + return G.Chain[ReaderIOEither[A]](f) +} + +func MonadChainFirst[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[A] { + return G.MonadChainFirst(ma, f) +} + +func ChainFirst[A, B any](f func(A) ReaderIOEither[B]) func(ReaderIOEither[A]) ReaderIOEither[A] { + return G.ChainFirst[ReaderIOEither[A]](f) +} + +func Of[A any](a A) ReaderIOEither[A] { + return G.Of[ReaderIOEither[A]](a) +} + +// MonadAp implements the `Ap` function for a reader with context. It creates a sub-context that will +// be canceled if any of the input operations errors out or +func MonadAp[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) ReaderIOEither[B] { + return G.MonadAp[ReaderIOEither[B]](fab, fa) +} + +func Ap[B, A any](fa ReaderIOEither[A]) func(ReaderIOEither[func(A) B]) ReaderIOEither[B] { + return G.Ap[ReaderIOEither[B], ReaderIOEither[func(A) B]](fa) +} + +func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) ReaderIOEither[A] { + return G.FromPredicate[ReaderIOEither[A]](pred, onFalse) +} + +func OrElse[A any](onLeft func(error) ReaderIOEither[A]) func(ReaderIOEither[A]) ReaderIOEither[A] { + return G.OrElse[ReaderIOEither[A]](onLeft) +} + +func Ask() ReaderIOEither[context.Context] { + return G.Ask[ReaderIOEither[context.Context]]() +} + +func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[B] { + return G.MonadChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](ma, f) +} + +func ChainEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] { + return G.ChainEitherK[ReaderIOEither[A], ReaderIOEither[B]](f) +} + +func MonadChainFirstEitherK[A, B any](ma ReaderIOEither[A], f func(A) ET.Either[error, B]) ReaderIOEither[A] { + return G.MonadChainFirstEitherK[ReaderIOEither[A]](ma, f) +} + +func ChainFirstEitherK[A, B any](f func(A) ET.Either[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[A] { + return G.ChainFirstEitherK[ReaderIOEither[A]](f) +} + +func ChainOptionK[A, B any](onNone func() error) func(func(A) O.Option[B]) func(ReaderIOEither[A]) ReaderIOEither[B] { + return G.ChainOptionK[ReaderIOEither[A], ReaderIOEither[B]](onNone) +} + +func FromIOEither[A any](t IOE.IOEither[error, A]) ReaderIOEither[A] { + return G.FromIOEither[ReaderIOEither[A]](t) +} + +func FromIO[A any](t IO.IO[A]) ReaderIOEither[A] { + return G.FromIO[ReaderIOEither[A]](t) +} + +func FromLazy[A any](t L.Lazy[A]) ReaderIOEither[A] { + return G.FromIO[ReaderIOEither[A]](t) +} + +// Never returns a 'ReaderIOEither' that never returns, except if its context gets canceled +func Never[A any]() ReaderIOEither[A] { + return G.Never[ReaderIOEither[A]]() +} + +func MonadChainIOK[A, B any](ma ReaderIOEither[A], f func(A) IO.IO[B]) ReaderIOEither[B] { + return G.MonadChainIOK[ReaderIOEither[B], ReaderIOEither[A]](ma, f) +} + +func ChainIOK[A, B any](f func(A) IO.IO[B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] { + return G.ChainIOK[ReaderIOEither[B], ReaderIOEither[A]](f) +} + +func MonadChainFirstIOK[A, B any](ma ReaderIOEither[A], f func(A) IO.IO[B]) ReaderIOEither[A] { + return G.MonadChainFirstIOK[ReaderIOEither[A]](ma, f) +} + +func ChainFirstIOK[A, B any](f func(A) IO.IO[B]) func(ma ReaderIOEither[A]) ReaderIOEither[A] { + return G.ChainFirstIOK[ReaderIOEither[A]](f) +} + +func ChainIOEitherK[A, B any](f func(A) IOE.IOEither[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] { + return G.ChainIOEitherK[ReaderIOEither[A], ReaderIOEither[B]](f) +} + +// Delay creates an operation that passes in the value after some delay +func Delay[A any](delay time.Duration) func(ma ReaderIOEither[A]) ReaderIOEither[A] { + return G.Delay[ReaderIOEither[A]](delay) +} + +// Timer will return the current time after an initial delay +func Timer(delay time.Duration) ReaderIOEither[time.Time] { + return G.Timer[ReaderIOEither[time.Time]](delay) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[A any](gen L.Lazy[ReaderIOEither[A]]) ReaderIOEither[A] { + return G.Defer[ReaderIOEither[A]](gen) +} + +// TryCatch wraps a reader returning a tuple as an error into ReaderIOEither +func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A] { + return G.TryCatch[ReaderIOEither[A]](f) +} + +// MonadAlt identifies an associative operation on a type constructor +func MonadAlt[A any](first ReaderIOEither[A], second L.Lazy[ReaderIOEither[A]]) ReaderIOEither[A] { + return G.MonadAlt(first, second) +} + +// Alt identifies an associative operation on a type constructor +func Alt[A any](second L.Lazy[ReaderIOEither[A]]) func(ReaderIOEither[A]) ReaderIOEither[A] { + return G.Alt(second) +} + +// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] { + return G.Memoize[ReaderIOEither[A]](rdr) +} + +// Flatten converts a nested [ReaderIOEither] into a [ReaderIOEither] +func Flatten[A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] { + return G.Flatten[ReaderIOEither[ReaderIOEither[A]]](rdr) +} + +func MonadFlap[B, A any](fab ReaderIOEither[func(A) B], a A) ReaderIOEither[B] { + return G.MonadFlap[ReaderIOEither[func(A) B], ReaderIOEither[B]](fab, a) +} + +func Flap[B, A any](a A) func(ReaderIOEither[func(A) B]) ReaderIOEither[B] { + return G.Flap[ReaderIOEither[func(A) B], ReaderIOEither[B]](a) +} + +func Fold[A, B any](onLeft func(error) ReaderIOEither[B], onRight func(A) ReaderIOEither[B]) func(ReaderIOEither[A]) ReaderIOEither[B] { + return G.Fold[ReaderIOEither[B], ReaderIOEither[A]](onLeft, onRight) +} + +func GetOrElse[A any](onLeft func(error) RIO.ReaderIO[context.Context, A]) func(ReaderIOEither[A]) RIO.ReaderIO[context.Context, A] { + return G.GetOrElse[RIO.ReaderIO[context.Context, A], ReaderIOEither[A]](onLeft) +} + +func OrLeft[A any](onLeft func(error) RIO.ReaderIO[context.Context, error]) func(ReaderIOEither[A]) ReaderIOEither[A] { + return G.OrLeft[ReaderIOEither[A]](onLeft) +} diff --git a/v2/context/readerioeither/reader_test.go b/v2/context/readerioeither/reader_test.go new file mode 100644 index 0000000..c61b8fe --- /dev/null +++ b/v2/context/readerioeither/reader_test.go @@ -0,0 +1,286 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + "fmt" + "testing" + "time" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func TestInnerContextCancelSemantics(t *testing.T) { + // start with a simple context + outer := context.Background() + + parent, parentCancel := context.WithCancel(outer) + defer parentCancel() + + inner, innerCancel := context.WithCancel(parent) + defer innerCancel() + + assert.NoError(t, parent.Err()) + assert.NoError(t, inner.Err()) + + innerCancel() + + assert.NoError(t, parent.Err()) + assert.Error(t, inner.Err()) + +} + +func TestOuterContextCancelSemantics(t *testing.T) { + // start with a simple context + outer := context.Background() + + parent, outerCancel := context.WithCancel(outer) + defer outerCancel() + + inner, innerCancel := context.WithCancel(parent) + defer innerCancel() + + assert.NoError(t, parent.Err()) + assert.NoError(t, inner.Err()) + + outerCancel() + + assert.Error(t, parent.Err()) + assert.Error(t, inner.Err()) + +} + +func TestOuterAndInnerContextCancelSemantics(t *testing.T) { + // start with a simple context + outer := context.Background() + + parent, outerCancel := context.WithCancel(outer) + defer outerCancel() + + inner, innerCancel := context.WithCancel(parent) + defer innerCancel() + + assert.NoError(t, parent.Err()) + assert.NoError(t, inner.Err()) + + outerCancel() + innerCancel() + + assert.Error(t, parent.Err()) + assert.Error(t, inner.Err()) + + outerCancel() + innerCancel() + + assert.Error(t, parent.Err()) + assert.Error(t, inner.Err()) +} + +func TestCancelCauseSemantics(t *testing.T) { + // start with a simple context + outer := context.Background() + + parent, outerCancel := context.WithCancelCause(outer) + defer outerCancel(nil) + + inner := context.WithValue(parent, "key", "value") + + assert.NoError(t, parent.Err()) + assert.NoError(t, inner.Err()) + + err := fmt.Errorf("test error") + + outerCancel(err) + + assert.Error(t, parent.Err()) + assert.Error(t, inner.Err()) + + assert.Equal(t, err, context.Cause(parent)) + assert.Equal(t, err, context.Cause(inner)) +} + +func TestTimer(t *testing.T) { + delta := 3 * time.Second + timer := Timer(delta) + ctx := context.Background() + + t0 := time.Now() + res := timer(ctx)() + t1 := time.Now() + + assert.WithinDuration(t, t0.Add(delta), t1, time.Second) + assert.True(t, E.IsRight(res)) +} + +func TestCanceledApply(t *testing.T) { + // our error + err := fmt.Errorf("TestCanceledApply") + // the actual apply value errors out after some time + errValue := F.Pipe1( + Left[string](err), + Delay[string](time.Second), + ) + // function never resolves + fct := Never[func(string) string]() + // apply the values, we expect an error after 1s + + applied := F.Pipe1( + fct, + Ap[string, string](errValue), + ) + + res := applied(context.Background())() + assert.Equal(t, E.Left[string](err), res) +} + +func TestRegularApply(t *testing.T) { + value := Of("Carsten") + fct := Of(utils.Upper) + + applied := F.Pipe1( + fct, + Ap[string, string](value), + ) + + res := applied(context.Background())() + assert.Equal(t, E.Of[error]("CARSTEN"), res) +} + +func TestWithResourceNoErrors(t *testing.T) { + var countAcquire, countBody, countRelease int + + acquire := FromLazy(func() int { + countAcquire++ + return countAcquire + }) + + release := func(int) ReaderIOEither[int] { + return FromLazy(func() int { + countRelease++ + return countRelease + }) + } + + body := func(int) ReaderIOEither[int] { + return FromLazy(func() int { + countBody++ + return countBody + }) + } + + resRIOE := WithResource[int](acquire, release)(body) + + res := resRIOE(context.Background())() + + assert.Equal(t, 1, countAcquire) + assert.Equal(t, 1, countBody) + assert.Equal(t, 1, countRelease) + assert.Equal(t, E.Of[error](1), res) +} + +func TestWithResourceErrorInBody(t *testing.T) { + var countAcquire, countBody, countRelease int + + acquire := FromLazy(func() int { + countAcquire++ + return countAcquire + }) + + release := func(int) ReaderIOEither[int] { + return FromLazy(func() int { + countRelease++ + return countRelease + }) + } + + err := fmt.Errorf("error in body") + body := func(int) ReaderIOEither[int] { + return Left[int](err) + } + + resRIOE := WithResource[int](acquire, release)(body) + + res := resRIOE(context.Background())() + + assert.Equal(t, 1, countAcquire) + assert.Equal(t, 0, countBody) + assert.Equal(t, 1, countRelease) + assert.Equal(t, E.Left[int](err), res) +} + +func TestWithResourceErrorInAcquire(t *testing.T) { + var countAcquire, countBody, countRelease int + + err := fmt.Errorf("error in acquire") + acquire := Left[int](err) + + release := func(int) ReaderIOEither[int] { + return FromLazy(func() int { + countRelease++ + return countRelease + }) + } + + body := func(int) ReaderIOEither[int] { + return FromLazy(func() int { + countBody++ + return countBody + }) + } + + resRIOE := WithResource[int](acquire, release)(body) + + res := resRIOE(context.Background())() + + assert.Equal(t, 0, countAcquire) + assert.Equal(t, 0, countBody) + assert.Equal(t, 0, countRelease) + assert.Equal(t, E.Left[int](err), res) +} + +func TestWithResourceErrorInRelease(t *testing.T) { + var countAcquire, countBody, countRelease int + + acquire := FromLazy(func() int { + countAcquire++ + return countAcquire + }) + + err := fmt.Errorf("error in release") + release := func(int) ReaderIOEither[int] { + return Left[int](err) + } + + body := func(int) ReaderIOEither[int] { + return FromLazy(func() int { + countBody++ + return countBody + }) + } + + resRIOE := WithResource[int](acquire, release)(body) + + res := resRIOE(context.Background())() + + assert.Equal(t, 1, countAcquire) + assert.Equal(t, 1, countBody) + assert.Equal(t, 0, countRelease) + assert.Equal(t, E.Left[int](err), res) +} diff --git a/v2/context/readerioeither/resource.go b/v2/context/readerioeither/resource.go new file mode 100644 index 0000000..4dacf38 --- /dev/null +++ b/v2/context/readerioeither/resource.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[A, R, ANY any](onCreate ReaderIOEither[R], onRelease func(R) ReaderIOEither[ANY]) func(func(R) ReaderIOEither[A]) ReaderIOEither[A] { + // wraps the callback functions with a context check + return G.WithResource[ReaderIOEither[A]](onCreate, onRelease) +} diff --git a/v2/context/readerioeither/resource_test.go b/v2/context/readerioeither/resource_test.go new file mode 100644 index 0000000..8386079 --- /dev/null +++ b/v2/context/readerioeither/resource_test.go @@ -0,0 +1,79 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + "io" + "os" + + B "github.com/IBM/fp-go/v2/bytes" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +var ( + openFile = F.Flow3( + IOE.Eitherize1(os.Open), + FromIOEither[*os.File], + ChainFirstIOK(F.Flow2( + (*os.File).Name, + IO.Logf[string]("Opened file [%s]"), + )), + ) +) + +func closeFile(f *os.File) ReaderIOEither[string] { + return F.Pipe1( + TryCatch(func(_ context.Context) func() (string, error) { + return func() (string, error) { + return f.Name(), f.Close() + } + }), + ChainFirstIOK(IO.Logf[string]("Closed file [%s]")), + ) +} + +func ExampleWithResource() { + + stringReader := WithResource[string](openFile("data/file.txt"), closeFile) + + rdr := stringReader(func(f *os.File) ReaderIOEither[string] { + return F.Pipe2( + TryCatch(func(_ context.Context) func() ([]byte, error) { + return func() ([]byte, error) { + return io.ReadAll(f) + } + }), + ChainFirstIOK(F.Flow2( + B.Size, + IO.Logf[int]("Read content of length [%d]"), + )), + Map(B.ToString), + ) + }) + + contentIOE := F.Pipe2( + context.Background(), + rdr, + IOE.ChainFirstIOK[error](IO.Printf[string]("Content: %s")), + ) + + contentIOE() + + // Output: Content: Carsten +} diff --git a/v2/context/readerioeither/semigroup.go b/v2/context/readerioeither/semigroup.go new file mode 100644 index 0000000..c24c117 --- /dev/null +++ b/v2/context/readerioeither/semigroup.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative +func AltSemigroup[A any]() S.Semigroup[ReaderIOEither[A]] { + return G.AltSemigroup[ReaderIOEither[A]]() +} diff --git a/v2/context/readerioeither/sync.go b/v2/context/readerioeither/sync.go new file mode 100644 index 0000000..73d0229 --- /dev/null +++ b/v2/context/readerioeither/sync.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[A any](lock ReaderIOEither[context.CancelFunc]) func(fa ReaderIOEither[A]) ReaderIOEither[A] { + return G.WithLock[ReaderIOEither[A]](lock) +} diff --git a/v2/context/readerioeither/traverse.go b/v2/context/readerioeither/traverse.go new file mode 100644 index 0000000..5aa08c9 --- /dev/null +++ b/v2/context/readerioeither/traverse.go @@ -0,0 +1,110 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/context/readerioeither/generic" +) + +// TraverseArray uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]] +func TraverseArray[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] { + return G.TraverseArray[[]A, ReaderIOEither[[]B]](f) +} + +// TraverseArrayWithIndex uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]] +func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] { + return G.TraverseArrayWithIndex[[]A, ReaderIOEither[[]B]](f) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] { + return G.SequenceArray[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma) +} + +// TraverseRecord uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]] +func TraverseRecord[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] { + return G.TraverseRecord[K, map[K]A, ReaderIOEither[map[K]B]](f) +} + +// TraverseRecordWithIndex uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]] +func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] { + return G.TraverseRecordWithIndex[K, map[K]A, ReaderIOEither[map[K]B]](f) +} + +// SequenceRecord converts a homogeneous sequence of either into an either of sequence +func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] { + return G.SequenceRecord[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma) +} + +// TraverseArraySeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]] +func TraverseArraySeq[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] { + return G.TraverseArraySeq[[]A, ReaderIOEither[[]B]](f) +} + +// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]] +func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] { + return G.TraverseArrayWithIndexSeq[[]A, ReaderIOEither[[]B]](f) +} + +// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence +func SequenceArraySeq[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] { + return G.SequenceArraySeq[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma) +} + +// TraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]] +func TraverseRecordSeq[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] { + return G.TraverseRecordSeq[K, map[K]A, ReaderIOEither[map[K]B]](f) +} + +// TraverseRecordWithIndexSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]] +func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] { + return G.TraverseRecordWithIndexSeq[K, map[K]A, ReaderIOEither[map[K]B]](f) +} + +// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence +func SequenceRecordSeq[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] { + return G.SequenceRecordSeq[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma) +} + +// TraverseArrayPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]] +func TraverseArrayPar[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] { + return G.TraverseArrayPar[[]A, ReaderIOEither[[]B]](f) +} + +// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]] +func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] { + return G.TraverseArrayWithIndexPar[[]A, ReaderIOEither[[]B]](f) +} + +// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence +func SequenceArrayPar[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] { + return G.SequenceArrayPar[[]A, []ReaderIOEither[A], ReaderIOEither[[]A]](ma) +} + +// TraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]] +func TraverseRecordPar[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] { + return G.TraverseRecordPar[K, map[K]A, ReaderIOEither[map[K]B]](f) +} + +// TraverseRecordWithIndexPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]] +func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] { + return G.TraverseRecordWithIndexPar[K, map[K]A, ReaderIOEither[map[K]B]](f) +} + +// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence +func SequenceRecordPar[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] { + return G.SequenceRecordPar[K, map[K]A, map[K]ReaderIOEither[A], ReaderIOEither[map[K]A]](ma) +} diff --git a/v2/context/readerioeither/type.go b/v2/context/readerioeither/type.go new file mode 100644 index 0000000..e743fd7 --- /dev/null +++ b/v2/context/readerioeither/type.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + + RE "github.com/IBM/fp-go/v2/readerioeither" +) + +// ReaderIOEither is a specialization of the [RE.ReaderIOEither] monad for the typical golang scenario in which the +// left value is an [error] and the context is a [context.Context] +type ReaderIOEither[A any] RE.ReaderIOEither[context.Context, error, A] diff --git a/v2/di/app.go b/v2/di/app.go new file mode 100644 index 0000000..2bd38ca --- /dev/null +++ b/v2/di/app.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package di + +import ( + DIE "github.com/IBM/fp-go/v2/di/erasure" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +var ( + // InjMain is the [InjectionToken] for the main application + InjMain = MakeToken[any]("APP") + + // Main is the resolver for the main application + Main = Resolve(InjMain) +) + +// RunMain runs the main application from a set of [DIE.Provider]s +var RunMain = F.Flow3( + DIE.MakeInjector, + Main, + IOE.Fold(IO.Of[error], F.Constant1[any](IO.Of[error](nil))), +) diff --git a/v2/di/doc.go b/v2/di/doc.go new file mode 100644 index 0000000..d156c65 --- /dev/null +++ b/v2/di/doc.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package di implements functions and data types supporting dependency injection patterns +// +// The fundamental building block is the concept of a [Dependency]. This describes the abstract concept of a function, service or value together with its type. +// Examples for dependencies can be as simple as configuration values such as the API URL for a service, the current username, a [Dependency] could be the map +// of the configuration environment, an http client or as complex as a service interface. Important is that a [Dependency] only defines the concept but +// not the implementation. +// +// The implementation of a [Dependency] is called a [Provider], the dependency of an `API URL` could e.g. be realized by a provider that consults the environment to read the information +// or a config file or simply hardcode it. +// In many cases the implementation of a [Provider] depends in turn on other [Dependency] (but never directly on other [Provider]s), a provider for an `API URL` that reads +// the information from the environment would e.g. depend on a [Dependency] that represents this environment. +// +// It is the resposibility of the [InjectableFactory] to create an instance of a [Dependency]. All instances are considered singletons. Create an [InjectableFactory] via the [MakeInjector] method. Use [Resolve] to create a strongly typed +// factory for a particular [InjectionToken]. +// +// In most cases it is not required to use [InjectableFactory] directly, instead you would create a [Provider] via the [MakeProvider2] method (suffix indicates the number of dependencies). In this call +// you give a number of (strongly typed) [Dependency] identifiers and a (strongly typed) factory function for the implementation. The dependency injection framework makes +// sure to resolve the dependencies before calling the factory method. +// +// For convenience purposes it can be helpful to attach a default implementation of a [Dependency]. In this case use the [MakeTokenWithDefault2] method (suffix indicates the number of dependencies) +// to define the [InjectionToken]. +// +// [Provider]: [github.com/IBM/fp-go/v2/di/erasure.Provider] +// [InjectableFactory]: [github.com/IBM/fp-go/v2/di/erasure.InjectableFactory] +// [MakeInjector]: [github.com/IBM/fp-go/v2/di/erasure.MakeInjector] +package di + +//go:generate go run .. di --count 15 --filename gen.go diff --git a/v2/di/erasure/injector.go b/v2/di/erasure/injector.go new file mode 100644 index 0000000..79480a4 --- /dev/null +++ b/v2/di/erasure/injector.go @@ -0,0 +1,170 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package erasure + +import ( + A "github.com/IBM/fp-go/v2/array" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity" + IG "github.com/IBM/fp-go/v2/identity/generic" + IOE "github.com/IBM/fp-go/v2/ioeither" + L "github.com/IBM/fp-go/v2/lazy" + O "github.com/IBM/fp-go/v2/option" + R "github.com/IBM/fp-go/v2/record" + T "github.com/IBM/fp-go/v2/tuple" + + "sync" +) + +func providerToEntry(p Provider) T.Tuple2[string, ProviderFactory] { + return T.MakeTuple2(p.Provides().Id(), p.Factory()) +} + +func itemProviderToMap(p Provider) map[string][]ProviderFactory { + return R.Singleton(p.Provides().Id(), A.Of(p.Factory())) +} + +var ( + // missingProviderError returns a [ProviderFactory] that fails due to a missing dependency + missingProviderError = F.Flow4( + Dependency.String, + errors.OnSome[string]("no provider for dependency [%s]"), + IOE.Left[any, error], + F.Constant1[InjectableFactory, IOE.IOEither[error, any]], + ) + + // missingProviderErrorOrDefault returns the default [ProviderFactory] or an error + missingProviderErrorOrDefault = F.Flow3( + T.Replicate2[Dependency], + T.Map2(Dependency.ProviderFactory, F.Flow2(missingProviderError, F.Constant[ProviderFactory])), + T.Tupled2(O.MonadGetOrElse[ProviderFactory]), + ) + + emptyMulti any = A.Empty[any]() + + // emptyMultiDependency returns a [ProviderFactory] for an empty, multi dependency + emptyMultiDependency = F.Constant1[Dependency](F.Constant1[InjectableFactory](IOE.Of[error](emptyMulti))) + + // handleMissingProvider covers the case of a missing provider. It either + // returns an error or an empty multi value provider + handleMissingProvider = F.Flow2( + F.Ternary(isMultiDependency, emptyMultiDependency, missingProviderErrorOrDefault), + F.Constant[ProviderFactory], + ) + + // mergeItemProviders is a monoid for item provider factories + mergeItemProviders = R.UnionMonoid[string](A.Semigroup[ProviderFactory]()) + + // mergeProviders is a monoid for provider factories + mergeProviders = R.UnionLastMonoid[string, ProviderFactory]() + + // collectItemProviders create a provider map for item providers + collectItemProviders = F.Flow2( + A.FoldMap[Provider](mergeItemProviders)(itemProviderToMap), + R.Map[string](itemProviderFactory), + ) + + // collectProviders collects non-item providers + collectProviders = F.Flow2( + A.Map(providerToEntry), + R.FromEntries[string, ProviderFactory], + ) + + // assembleProviders constructs the provider map for item and non-item providers + assembleProviders = F.Flow3( + A.Partition(isItemProvider), + T.Map2(collectProviders, collectItemProviders), + T.Tupled2(mergeProviders.Concat), + ) +) + +// isMultiDependency tests if a dependency is a container dependency +func isMultiDependency(dep Dependency) bool { + return dep.Flag()&Multi == Multi +} + +// isItemProvider tests if a provivder provides a single item +func isItemProvider(provider Provider) bool { + return provider.Provides().Flag()&Item == Item +} + +// itemProviderFactory combines multiple factories into one, returning an array +func itemProviderFactory(fcts []ProviderFactory) ProviderFactory { + return func(inj InjectableFactory) IOE.IOEither[error, any] { + return F.Pipe2( + fcts, + IOE.TraverseArray(I.Flap[IOE.IOEither[error, any]](inj)), + IOE.Map[error](F.ToAny[[]any]), + ) + } +} + +// MakeInjector creates an [InjectableFactory] based on a set of [Provider]s +// +// The resulting [InjectableFactory] can then be used to retrieve service instances given their [Dependency]. The implementation +// makes sure to transitively resolve the required dependencies. +func MakeInjector(providers []Provider) InjectableFactory { + + type Result = IOE.IOEither[error, any] + type LazyResult = L.Lazy[Result] + + // resolved stores the values resolved so far, key is the string ID + // of the token, value is a lazy result + var resolved sync.Map + + // provide a mapping for all providers + factoryByID := assembleProviders(providers) + + // the actual factory, we need lazy initialization + var injFct InjectableFactory + + // lazy initialization, so we can cross reference it + injFct = func(token Dependency) Result { + + key := token.Id() + + // according to https://github.com/golang/go/issues/44159 this + // is the best way to use the sync map + actual, loaded := resolved.Load(key) + if !loaded { + + computeResult := L.MakeLazy(func() Result { + return F.Pipe5( + token, + T.Replicate2[Dependency], + T.Map2(F.Flow3( + Dependency.Id, + R.Lookup[ProviderFactory, string], + I.Ap[O.Option[ProviderFactory]](factoryByID), + ), handleMissingProvider), + T.Tupled2(O.MonadGetOrElse[ProviderFactory]), + IG.Ap[ProviderFactory](injFct), + IOE.Memoize[error, any], + ) + }) + + actual, _ = resolved.LoadOrStore(key, F.Pipe1( + computeResult, + L.Memoize[Result], + )) + } + + return actual.(LazyResult)() + } + + return injFct +} diff --git a/v2/di/erasure/provider.go b/v2/di/erasure/provider.go new file mode 100644 index 0000000..20927e1 --- /dev/null +++ b/v2/di/erasure/provider.go @@ -0,0 +1,181 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package erasure + +import ( + "fmt" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity" + IO "github.com/IBM/fp-go/v2/io" + IOG "github.com/IBM/fp-go/v2/io/generic" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOO "github.com/IBM/fp-go/v2/iooption" + Int "github.com/IBM/fp-go/v2/number/integer" + O "github.com/IBM/fp-go/v2/option" + R "github.com/IBM/fp-go/v2/record" +) + +type ( + // InjectableFactory is a factory function that can create an untyped instance of a service based on its [Dependency] identifier + InjectableFactory = func(Dependency) IOE.IOEither[error, any] + ProviderFactory = func(InjectableFactory) IOE.IOEither[error, any] + + paramIndex = map[int]int + paramValue = map[int]any + handler = func(paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] + mapping = map[int]paramIndex + + Provider interface { + fmt.Stringer + // Provides returns the [Dependency] implemented by this provider + Provides() Dependency + // Factory returns s function that can create an instance of the dependency based on an [InjectableFactory] + Factory() ProviderFactory + } + + provider struct { + provides Dependency + factory ProviderFactory + } +) + +func (p *provider) Provides() Dependency { + return p.provides +} + +func (p *provider) Factory() ProviderFactory { + return p.factory +} + +func (p *provider) String() string { + return fmt.Sprintf("Provider for [%s]", p.provides) +} + +func MakeProvider(token Dependency, fct ProviderFactory) Provider { + return &provider{token, fct} +} + +func mapFromToken(idx int, token Dependency) map[int]paramIndex { + return R.Singleton(token.Flag()&BehaviourMask, R.Singleton(idx, idx)) +} + +var ( + // Empty is the empty array of providers + Empty = A.Empty[Provider]() + + mergeTokenMaps = R.UnionMonoid[int](R.UnionLastSemigroup[int, int]()) + foldDeps = A.FoldMapWithIndex[Dependency](mergeTokenMaps)(mapFromToken) + mergeMaps = R.UnionLastMonoid[int, any]() + collectParams = R.CollectOrd[any, any](Int.Ord)(F.SK[int, any]) + + mapDeps = F.Curry2(A.MonadMap[Dependency, IOE.IOEither[error, any]]) + + handlers = map[int]handler{ + Identity: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return F.Pipe1( + mp, + IOE.TraverseRecord[int](getAt(res)), + ) + } + }, + Option: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return F.Pipe3( + mp, + IOG.TraverseRecord[IO.IO[map[int]E.Either[error, any]], paramIndex](getAt(res)), + IO.Map(R.Map[int](F.Flow2( + E.ToOption[error, any], + F.ToAny[O.Option[any]], + ))), + IOE.FromIO[error, paramValue], + ) + } + }, + IOEither: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return F.Pipe2( + mp, + R.Map[int](F.Flow2( + getAt(res), + F.ToAny[IOE.IOEither[error, any]], + )), + IOE.Of[error, paramValue], + ) + } + }, + IOOption: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return F.Pipe2( + mp, + R.Map[int](F.Flow3( + getAt(res), + IOE.ToIOOption[error, any], + F.ToAny[IOO.IOOption[any]], + )), + IOE.Of[error, paramValue], + ) + } + }, + } +) + +func getAt[T any](ar []T) func(idx int) T { + return func(idx int) T { + return ar[idx] + } +} + +func handleMapping(mp mapping) func(res []IOE.IOEither[error, any]) IOE.IOEither[error, []any] { + preFct := F.Pipe1( + mp, + R.Collect(func(idx int, p paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] { + return handlers[idx](p) + }), + ) + doFct := F.Flow2( + I.Flap[IOE.IOEither[error, paramValue], []IOE.IOEither[error, any]], + IOE.TraverseArray[error, func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue], paramValue], + ) + postFct := IOE.Map[error](F.Flow2( + A.Fold(mergeMaps), + collectParams, + )) + + return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, []any] { + return F.Pipe2( + preFct, + doFct(res), + postFct, + ) + } +} + +// MakeProviderFactory constructs a [ProviderFactory] based on a set of [Dependency]s and +// a function that accepts the resolved dependencies to return a result +func MakeProviderFactory( + deps []Dependency, + fct func(param ...any) IOE.IOEither[error, any]) ProviderFactory { + + return F.Flow3( + mapDeps(deps), + handleMapping(foldDeps(deps)), + IOE.Chain(F.Unvariadic0(fct)), + ) +} diff --git a/v2/di/erasure/token.go b/v2/di/erasure/token.go new file mode 100644 index 0000000..12de02b --- /dev/null +++ b/v2/di/erasure/token.go @@ -0,0 +1,45 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package erasure + +import ( + "fmt" + + O "github.com/IBM/fp-go/v2/option" +) + +const ( + BehaviourMask = 0x0f + Identity = 0 // required dependency + Option = 1 // optional dependency + IOEither = 2 // lazy and required + IOOption = 3 // lazy and optional + + TypeMask = 0xf0 + Multi = 1 << 4 // array of implementations + Item = 2 << 4 // item of a multi token +) + +// Dependency describes the relationship to a service +type Dependency interface { + fmt.Stringer + // Id returns a unique identifier for a token that can be used as a cache key + Id() string + // Flag returns a tag that identifies the behaviour of the dependency + Flag() int + // ProviderFactory optionally returns an attached [ProviderFactory] that represents the default for this dependency + ProviderFactory() O.Option[ProviderFactory] +} diff --git a/v2/di/gen.go b/v2/di/gen.go new file mode 100644 index 0000000..04584dc --- /dev/null +++ b/v2/di/gen.go @@ -0,0 +1,1888 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:18:47.9991921 +0100 CET m=+0.012914901 + +package di + +import ( + A "github.com/IBM/fp-go/v2/array" + DIE "github.com/IBM/fp-go/v2/di/erasure" + E "github.com/IBM/fp-go/v2/either" + IOE "github.com/IBM/fp-go/v2/ioeither" + T "github.com/IBM/fp-go/v2/tuple" +) + +// eraseProviderFactory1 creates a function that takes a variadic number of untyped arguments and from a function of 1 strongly typed arguments and 1 dependencies +func eraseProviderFactory1[T1 any, R any]( + d1 Dependency[T1], + f func(T1) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled1(f)) + t1 := lookupAt[T1](0, d1) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT1( + t1(params), + )) + } +} + +// MakeProviderFactory1 creates a [DIE.ProviderFactory] from a function with 1 arguments and 1 dependencies +func MakeProviderFactory1[T1 any, R any]( + d1 Dependency[T1], + f func(T1) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + ), + eraseProviderFactory1( + d1, + f, + ), + ) +} + +// MakeTokenWithDefault1 creates an [InjectionToken] with a default implementation with 1 dependencies +func MakeTokenWithDefault1[T1 any, R any]( + name string, + d1 Dependency[T1], + f func(T1) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory1( + d1, + f, + )) +} + +// MakeProvider1 creates a [DIE.Provider] for an [InjectionToken] from a function with 1 dependencies +func MakeProvider1[T1 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + f func(T1) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory1( + d1, + f, + )) +} + +// eraseProviderFactory2 creates a function that takes a variadic number of untyped arguments and from a function of 2 strongly typed arguments and 2 dependencies +func eraseProviderFactory2[T1, T2 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + f func(T1, T2) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled2(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT2( + t1(params), + t2(params), + )) + } +} + +// MakeProviderFactory2 creates a [DIE.ProviderFactory] from a function with 2 arguments and 2 dependencies +func MakeProviderFactory2[T1, T2 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + f func(T1, T2) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + ), + eraseProviderFactory2( + d1, + d2, + f, + ), + ) +} + +// MakeTokenWithDefault2 creates an [InjectionToken] with a default implementation with 2 dependencies +func MakeTokenWithDefault2[T1, T2 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + f func(T1, T2) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory2( + d1, + d2, + f, + )) +} + +// MakeProvider2 creates a [DIE.Provider] for an [InjectionToken] from a function with 2 dependencies +func MakeProvider2[T1, T2 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + f func(T1, T2) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory2( + d1, + d2, + f, + )) +} + +// eraseProviderFactory3 creates a function that takes a variadic number of untyped arguments and from a function of 3 strongly typed arguments and 3 dependencies +func eraseProviderFactory3[T1, T2, T3 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + f func(T1, T2, T3) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled3(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT3( + t1(params), + t2(params), + t3(params), + )) + } +} + +// MakeProviderFactory3 creates a [DIE.ProviderFactory] from a function with 3 arguments and 3 dependencies +func MakeProviderFactory3[T1, T2, T3 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + f func(T1, T2, T3) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + ), + eraseProviderFactory3( + d1, + d2, + d3, + f, + ), + ) +} + +// MakeTokenWithDefault3 creates an [InjectionToken] with a default implementation with 3 dependencies +func MakeTokenWithDefault3[T1, T2, T3 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + f func(T1, T2, T3) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory3( + d1, + d2, + d3, + f, + )) +} + +// MakeProvider3 creates a [DIE.Provider] for an [InjectionToken] from a function with 3 dependencies +func MakeProvider3[T1, T2, T3 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + f func(T1, T2, T3) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory3( + d1, + d2, + d3, + f, + )) +} + +// eraseProviderFactory4 creates a function that takes a variadic number of untyped arguments and from a function of 4 strongly typed arguments and 4 dependencies +func eraseProviderFactory4[T1, T2, T3, T4 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + f func(T1, T2, T3, T4) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled4(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT4( + t1(params), + t2(params), + t3(params), + t4(params), + )) + } +} + +// MakeProviderFactory4 creates a [DIE.ProviderFactory] from a function with 4 arguments and 4 dependencies +func MakeProviderFactory4[T1, T2, T3, T4 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + f func(T1, T2, T3, T4) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + ), + eraseProviderFactory4( + d1, + d2, + d3, + d4, + f, + ), + ) +} + +// MakeTokenWithDefault4 creates an [InjectionToken] with a default implementation with 4 dependencies +func MakeTokenWithDefault4[T1, T2, T3, T4 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + f func(T1, T2, T3, T4) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory4( + d1, + d2, + d3, + d4, + f, + )) +} + +// MakeProvider4 creates a [DIE.Provider] for an [InjectionToken] from a function with 4 dependencies +func MakeProvider4[T1, T2, T3, T4 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + f func(T1, T2, T3, T4) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory4( + d1, + d2, + d3, + d4, + f, + )) +} + +// eraseProviderFactory5 creates a function that takes a variadic number of untyped arguments and from a function of 5 strongly typed arguments and 5 dependencies +func eraseProviderFactory5[T1, T2, T3, T4, T5 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + f func(T1, T2, T3, T4, T5) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled5(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT5( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + )) + } +} + +// MakeProviderFactory5 creates a [DIE.ProviderFactory] from a function with 5 arguments and 5 dependencies +func MakeProviderFactory5[T1, T2, T3, T4, T5 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + f func(T1, T2, T3, T4, T5) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + ), + eraseProviderFactory5( + d1, + d2, + d3, + d4, + d5, + f, + ), + ) +} + +// MakeTokenWithDefault5 creates an [InjectionToken] with a default implementation with 5 dependencies +func MakeTokenWithDefault5[T1, T2, T3, T4, T5 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + f func(T1, T2, T3, T4, T5) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory5( + d1, + d2, + d3, + d4, + d5, + f, + )) +} + +// MakeProvider5 creates a [DIE.Provider] for an [InjectionToken] from a function with 5 dependencies +func MakeProvider5[T1, T2, T3, T4, T5 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + f func(T1, T2, T3, T4, T5) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory5( + d1, + d2, + d3, + d4, + d5, + f, + )) +} + +// eraseProviderFactory6 creates a function that takes a variadic number of untyped arguments and from a function of 6 strongly typed arguments and 6 dependencies +func eraseProviderFactory6[T1, T2, T3, T4, T5, T6 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + f func(T1, T2, T3, T4, T5, T6) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled6(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT6( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + )) + } +} + +// MakeProviderFactory6 creates a [DIE.ProviderFactory] from a function with 6 arguments and 6 dependencies +func MakeProviderFactory6[T1, T2, T3, T4, T5, T6 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + f func(T1, T2, T3, T4, T5, T6) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + ), + eraseProviderFactory6( + d1, + d2, + d3, + d4, + d5, + d6, + f, + ), + ) +} + +// MakeTokenWithDefault6 creates an [InjectionToken] with a default implementation with 6 dependencies +func MakeTokenWithDefault6[T1, T2, T3, T4, T5, T6 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + f func(T1, T2, T3, T4, T5, T6) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory6( + d1, + d2, + d3, + d4, + d5, + d6, + f, + )) +} + +// MakeProvider6 creates a [DIE.Provider] for an [InjectionToken] from a function with 6 dependencies +func MakeProvider6[T1, T2, T3, T4, T5, T6 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + f func(T1, T2, T3, T4, T5, T6) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory6( + d1, + d2, + d3, + d4, + d5, + d6, + f, + )) +} + +// eraseProviderFactory7 creates a function that takes a variadic number of untyped arguments and from a function of 7 strongly typed arguments and 7 dependencies +func eraseProviderFactory7[T1, T2, T3, T4, T5, T6, T7 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + f func(T1, T2, T3, T4, T5, T6, T7) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled7(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT7( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + )) + } +} + +// MakeProviderFactory7 creates a [DIE.ProviderFactory] from a function with 7 arguments and 7 dependencies +func MakeProviderFactory7[T1, T2, T3, T4, T5, T6, T7 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + f func(T1, T2, T3, T4, T5, T6, T7) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + ), + eraseProviderFactory7( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + f, + ), + ) +} + +// MakeTokenWithDefault7 creates an [InjectionToken] with a default implementation with 7 dependencies +func MakeTokenWithDefault7[T1, T2, T3, T4, T5, T6, T7 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + f func(T1, T2, T3, T4, T5, T6, T7) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory7( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + f, + )) +} + +// MakeProvider7 creates a [DIE.Provider] for an [InjectionToken] from a function with 7 dependencies +func MakeProvider7[T1, T2, T3, T4, T5, T6, T7 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + f func(T1, T2, T3, T4, T5, T6, T7) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory7( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + f, + )) +} + +// eraseProviderFactory8 creates a function that takes a variadic number of untyped arguments and from a function of 8 strongly typed arguments and 8 dependencies +func eraseProviderFactory8[T1, T2, T3, T4, T5, T6, T7, T8 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + f func(T1, T2, T3, T4, T5, T6, T7, T8) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled8(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + t8 := lookupAt[T8](7, d8) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT8( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + t8(params), + )) + } +} + +// MakeProviderFactory8 creates a [DIE.ProviderFactory] from a function with 8 arguments and 8 dependencies +func MakeProviderFactory8[T1, T2, T3, T4, T5, T6, T7, T8 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + f func(T1, T2, T3, T4, T5, T6, T7, T8) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + ), + eraseProviderFactory8( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + f, + ), + ) +} + +// MakeTokenWithDefault8 creates an [InjectionToken] with a default implementation with 8 dependencies +func MakeTokenWithDefault8[T1, T2, T3, T4, T5, T6, T7, T8 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + f func(T1, T2, T3, T4, T5, T6, T7, T8) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory8( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + f, + )) +} + +// MakeProvider8 creates a [DIE.Provider] for an [InjectionToken] from a function with 8 dependencies +func MakeProvider8[T1, T2, T3, T4, T5, T6, T7, T8 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + f func(T1, T2, T3, T4, T5, T6, T7, T8) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory8( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + f, + )) +} + +// eraseProviderFactory9 creates a function that takes a variadic number of untyped arguments and from a function of 9 strongly typed arguments and 9 dependencies +func eraseProviderFactory9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled9(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + t8 := lookupAt[T8](7, d8) + t9 := lookupAt[T9](8, d9) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT9( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + t8(params), + t9(params), + )) + } +} + +// MakeProviderFactory9 creates a [DIE.ProviderFactory] from a function with 9 arguments and 9 dependencies +func MakeProviderFactory9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + ), + eraseProviderFactory9( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + f, + ), + ) +} + +// MakeTokenWithDefault9 creates an [InjectionToken] with a default implementation with 9 dependencies +func MakeTokenWithDefault9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory9( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + f, + )) +} + +// MakeProvider9 creates a [DIE.Provider] for an [InjectionToken] from a function with 9 dependencies +func MakeProvider9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory9( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + f, + )) +} + +// eraseProviderFactory10 creates a function that takes a variadic number of untyped arguments and from a function of 10 strongly typed arguments and 10 dependencies +func eraseProviderFactory10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled10(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + t8 := lookupAt[T8](7, d8) + t9 := lookupAt[T9](8, d9) + t10 := lookupAt[T10](9, d10) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT10( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + t8(params), + t9(params), + t10(params), + )) + } +} + +// MakeProviderFactory10 creates a [DIE.ProviderFactory] from a function with 10 arguments and 10 dependencies +func MakeProviderFactory10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + ), + eraseProviderFactory10( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + f, + ), + ) +} + +// MakeTokenWithDefault10 creates an [InjectionToken] with a default implementation with 10 dependencies +func MakeTokenWithDefault10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory10( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + f, + )) +} + +// MakeProvider10 creates a [DIE.Provider] for an [InjectionToken] from a function with 10 dependencies +func MakeProvider10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory10( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + f, + )) +} + +// eraseProviderFactory11 creates a function that takes a variadic number of untyped arguments and from a function of 11 strongly typed arguments and 11 dependencies +func eraseProviderFactory11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled11(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + t8 := lookupAt[T8](7, d8) + t9 := lookupAt[T9](8, d9) + t10 := lookupAt[T10](9, d10) + t11 := lookupAt[T11](10, d11) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT11( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + t8(params), + t9(params), + t10(params), + t11(params), + )) + } +} + +// MakeProviderFactory11 creates a [DIE.ProviderFactory] from a function with 11 arguments and 11 dependencies +func MakeProviderFactory11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + ), + eraseProviderFactory11( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + f, + ), + ) +} + +// MakeTokenWithDefault11 creates an [InjectionToken] with a default implementation with 11 dependencies +func MakeTokenWithDefault11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory11( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + f, + )) +} + +// MakeProvider11 creates a [DIE.Provider] for an [InjectionToken] from a function with 11 dependencies +func MakeProvider11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory11( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + f, + )) +} + +// eraseProviderFactory12 creates a function that takes a variadic number of untyped arguments and from a function of 12 strongly typed arguments and 12 dependencies +func eraseProviderFactory12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled12(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + t8 := lookupAt[T8](7, d8) + t9 := lookupAt[T9](8, d9) + t10 := lookupAt[T10](9, d10) + t11 := lookupAt[T11](10, d11) + t12 := lookupAt[T12](11, d12) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT12( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + t8(params), + t9(params), + t10(params), + t11(params), + t12(params), + )) + } +} + +// MakeProviderFactory12 creates a [DIE.ProviderFactory] from a function with 12 arguments and 12 dependencies +func MakeProviderFactory12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + ), + eraseProviderFactory12( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + f, + ), + ) +} + +// MakeTokenWithDefault12 creates an [InjectionToken] with a default implementation with 12 dependencies +func MakeTokenWithDefault12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory12( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + f, + )) +} + +// MakeProvider12 creates a [DIE.Provider] for an [InjectionToken] from a function with 12 dependencies +func MakeProvider12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory12( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + f, + )) +} + +// eraseProviderFactory13 creates a function that takes a variadic number of untyped arguments and from a function of 13 strongly typed arguments and 13 dependencies +func eraseProviderFactory13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled13(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + t8 := lookupAt[T8](7, d8) + t9 := lookupAt[T9](8, d9) + t10 := lookupAt[T10](9, d10) + t11 := lookupAt[T11](10, d11) + t12 := lookupAt[T12](11, d12) + t13 := lookupAt[T13](12, d13) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT13( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + t8(params), + t9(params), + t10(params), + t11(params), + t12(params), + t13(params), + )) + } +} + +// MakeProviderFactory13 creates a [DIE.ProviderFactory] from a function with 13 arguments and 13 dependencies +func MakeProviderFactory13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + ), + eraseProviderFactory13( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + f, + ), + ) +} + +// MakeTokenWithDefault13 creates an [InjectionToken] with a default implementation with 13 dependencies +func MakeTokenWithDefault13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory13( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + f, + )) +} + +// MakeProvider13 creates a [DIE.Provider] for an [InjectionToken] from a function with 13 dependencies +func MakeProvider13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory13( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + f, + )) +} + +// eraseProviderFactory14 creates a function that takes a variadic number of untyped arguments and from a function of 14 strongly typed arguments and 14 dependencies +func eraseProviderFactory14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + d14 Dependency[T14], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled14(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + t8 := lookupAt[T8](7, d8) + t9 := lookupAt[T9](8, d9) + t10 := lookupAt[T10](9, d10) + t11 := lookupAt[T11](10, d11) + t12 := lookupAt[T12](11, d12) + t13 := lookupAt[T13](12, d13) + t14 := lookupAt[T14](13, d14) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT14( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + t8(params), + t9(params), + t10(params), + t11(params), + t12(params), + t13(params), + t14(params), + )) + } +} + +// MakeProviderFactory14 creates a [DIE.ProviderFactory] from a function with 14 arguments and 14 dependencies +func MakeProviderFactory14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + d14 Dependency[T14], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + ), + eraseProviderFactory14( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + f, + ), + ) +} + +// MakeTokenWithDefault14 creates an [InjectionToken] with a default implementation with 14 dependencies +func MakeTokenWithDefault14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + d14 Dependency[T14], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory14( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + f, + )) +} + +// MakeProvider14 creates a [DIE.Provider] for an [InjectionToken] from a function with 14 dependencies +func MakeProvider14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + d14 Dependency[T14], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory14( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + f, + )) +} + +// eraseProviderFactory15 creates a function that takes a variadic number of untyped arguments and from a function of 15 strongly typed arguments and 15 dependencies +func eraseProviderFactory15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + d14 Dependency[T14], + d15 Dependency[T15], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + ft := eraseTuple(T.Tupled15(f)) + t1 := lookupAt[T1](0, d1) + t2 := lookupAt[T2](1, d2) + t3 := lookupAt[T3](2, d3) + t4 := lookupAt[T4](3, d4) + t5 := lookupAt[T5](4, d5) + t6 := lookupAt[T6](5, d6) + t7 := lookupAt[T7](6, d7) + t8 := lookupAt[T8](7, d8) + t9 := lookupAt[T9](8, d9) + t10 := lookupAt[T10](9, d10) + t11 := lookupAt[T11](10, d11) + t12 := lookupAt[T12](11, d12) + t13 := lookupAt[T13](12, d13) + t14 := lookupAt[T14](13, d14) + t15 := lookupAt[T15](14, d15) + return func(params ...any) IOE.IOEither[error, any] { + return ft(E.SequenceT15( + t1(params), + t2(params), + t3(params), + t4(params), + t5(params), + t6(params), + t7(params), + t8(params), + t9(params), + t10(params), + t11(params), + t12(params), + t13(params), + t14(params), + t15(params), + )) + } +} + +// MakeProviderFactory15 creates a [DIE.ProviderFactory] from a function with 15 arguments and 15 dependencies +func MakeProviderFactory15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any, R any]( + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + d14 Dependency[T14], + d15 Dependency[T15], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.From[DIE.Dependency]( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + ), + eraseProviderFactory15( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + f, + ), + ) +} + +// MakeTokenWithDefault15 creates an [InjectionToken] with a default implementation with 15 dependencies +func MakeTokenWithDefault15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any, R any]( + name string, + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + d14 Dependency[T14], + d15 Dependency[T15], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) IOE.IOEither[error, R], +) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory15( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + f, + )) +} + +// MakeProvider15 creates a [DIE.Provider] for an [InjectionToken] from a function with 15 dependencies +func MakeProvider15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any, R any]( + token InjectionToken[R], + d1 Dependency[T1], + d2 Dependency[T2], + d3 Dependency[T3], + d4 Dependency[T4], + d5 Dependency[T5], + d6 Dependency[T6], + d7 Dependency[T7], + d8 Dependency[T8], + d9 Dependency[T9], + d10 Dependency[T10], + d11 Dependency[T11], + d12 Dependency[T12], + d13 Dependency[T13], + d14 Dependency[T14], + d15 Dependency[T15], + f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory15( + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + f, + )) +} diff --git a/v2/di/injector.go b/v2/di/injector.go new file mode 100644 index 0000000..9bff943 --- /dev/null +++ b/v2/di/injector.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package di + +import ( + DIE "github.com/IBM/fp-go/v2/di/erasure" + F "github.com/IBM/fp-go/v2/function" + IG "github.com/IBM/fp-go/v2/identity/generic" + IOE "github.com/IBM/fp-go/v2/ioeither" + RIOE "github.com/IBM/fp-go/v2/readerioeither" +) + +// Resolve performs a type safe resolution of a dependency +func Resolve[T any](token InjectionToken[T]) RIOE.ReaderIOEither[DIE.InjectableFactory, error, T] { + return F.Flow2( + IG.Ap[DIE.InjectableFactory](asDependency(token)), + IOE.ChainEitherK(token.Unerase), + ) +} diff --git a/v2/di/provider.go b/v2/di/provider.go new file mode 100644 index 0000000..6d07924 --- /dev/null +++ b/v2/di/provider.go @@ -0,0 +1,79 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package di + +import ( + A "github.com/IBM/fp-go/v2/array" + DIE "github.com/IBM/fp-go/v2/di/erasure" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +func lookupAt[T any](idx int, token Dependency[T]) func(params []any) E.Either[error, T] { + return F.Flow3( + A.Lookup[any](idx), + E.FromOption[any](errors.OnNone("No parameter at position %d", idx)), + E.Chain(token.Unerase), + ) +} + +func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error, A]) IOE.IOEither[error, any] { + return F.Flow3( + IOE.FromEither[error, A], + IOE.Chain(f), + IOE.Map[error](F.ToAny[R]), + ) +} + +func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] { + return func(_ ...any) IOE.IOEither[error, any] { + return F.Pipe1( + f, + IOE.Map[error](F.ToAny[R]), + ) + } +} + +func MakeProviderFactory0[R any]( + fct IOE.IOEither[error, R], +) DIE.ProviderFactory { + return DIE.MakeProviderFactory( + A.Empty[DIE.Dependency](), + eraseProviderFactory0(fct), + ) +} + +// MakeTokenWithDefault0 creates a unique [InjectionToken] for a specific type with an attached default [DIE.Provider] +func MakeTokenWithDefault0[R any](name string, fct IOE.IOEither[error, R]) InjectionToken[R] { + return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct)) +} + +func MakeProvider0[R any]( + token InjectionToken[R], + fct IOE.IOEither[error, R], +) DIE.Provider { + return DIE.MakeProvider( + token, + MakeProviderFactory0(fct), + ) +} + +// ConstProvider simple implementation for a provider with a constant value +func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider { + return MakeProvider0[R](token, IOE.Of[error](value)) +} diff --git a/v2/di/provider_test.go b/v2/di/provider_test.go new file mode 100644 index 0000000..86454dd --- /dev/null +++ b/v2/di/provider_test.go @@ -0,0 +1,346 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package di + +import ( + "fmt" + "testing" + "time" + + A "github.com/IBM/fp-go/v2/array" + DIE "github.com/IBM/fp-go/v2/di/erasure" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +var ( + INJ_KEY2 = MakeToken[string]("INJ_KEY2") + INJ_KEY1 = MakeToken[string]("INJ_KEY1") + INJ_KEY3 = MakeToken[string]("INJ_KEY3") +) + +func TestSimpleProvider(t *testing.T) { + + var staticCount int + + staticValue := func(value string) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + staticCount++ + return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now())) + } + } + + var dynamicCount int + + dynamicValue := func(value string) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + dynamicCount++ + return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now())) + } + } + + p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten")) + p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue) + + inj := DIE.MakeInjector(A.From(p1, p2)) + + i1 := Resolve(INJ_KEY1) + i2 := Resolve(INJ_KEY2) + + res := IOE.SequenceT4( + i2(inj), + i1(inj), + i2(inj), + i1(inj), + ) + + r := res() + + assert.True(t, E.IsRight(r)) + assert.Equal(t, 1, staticCount) + assert.Equal(t, 1, dynamicCount) +} + +func TestOptionalProvider(t *testing.T) { + + var staticCount int + + staticValue := func(value string) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + staticCount++ + return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now())) + } + } + + var dynamicCount int + + dynamicValue := func(value O.Option[string]) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + dynamicCount++ + return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now())) + } + } + + p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten")) + p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Option(), dynamicValue) + + inj := DIE.MakeInjector(A.From(p1, p2)) + + i1 := Resolve(INJ_KEY1) + i2 := Resolve(INJ_KEY2) + + res := IOE.SequenceT4( + i2(inj), + i1(inj), + i2(inj), + i1(inj), + ) + + r := res() + + assert.True(t, E.IsRight(r)) + assert.Equal(t, 1, staticCount) + assert.Equal(t, 1, dynamicCount) +} + +func TestOptionalProviderMissingDependency(t *testing.T) { + + var dynamicCount int + + dynamicValue := func(value O.Option[string]) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + dynamicCount++ + return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now())) + } + } + + p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Option(), dynamicValue) + + inj := DIE.MakeInjector(A.From(p2)) + + i2 := Resolve(INJ_KEY2) + + res := IOE.SequenceT2( + i2(inj), + i2(inj), + ) + + r := res() + + assert.True(t, E.IsRight(r)) + assert.Equal(t, 1, dynamicCount) +} + +func TestProviderMissingDependency(t *testing.T) { + + var dynamicCount int + + dynamicValue := func(value string) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + dynamicCount++ + return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now())) + } + } + + p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue) + + inj := DIE.MakeInjector(A.From(p2)) + + i2 := Resolve(INJ_KEY2) + + res := IOE.SequenceT2( + i2(inj), + i2(inj), + ) + + r := res() + + assert.True(t, E.IsLeft(r)) + assert.Equal(t, 0, dynamicCount) +} + +func TestEagerAndLazyProvider(t *testing.T) { + + var staticCount int + + staticValue := func(value string) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + staticCount++ + return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now())) + } + } + + var dynamicCount int + + dynamicValue := func(value string) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + dynamicCount++ + return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now())) + } + } + + var lazyEagerCount int + + lazyEager := func(laz IOE.IOEither[error, string], eager string) IOE.IOEither[error, string] { + return F.Pipe1( + laz, + IOE.Chain(func(lazValue string) IOE.IOEither[error, string] { + return func() E.Either[error, string] { + lazyEagerCount++ + return E.Of[error](fmt.Sprintf("Dynamic based on [%s], [%s] at [%s]", lazValue, eager, time.Now())) + } + }), + ) + } + + p1 := MakeProvider0(INJ_KEY1, staticValue("Carsten")) + p2 := MakeProvider1(INJ_KEY2, INJ_KEY1.Identity(), dynamicValue) + p3 := MakeProvider2(INJ_KEY3, INJ_KEY2.IOEither(), INJ_KEY1.Identity(), lazyEager) + + inj := DIE.MakeInjector(A.From(p1, p2, p3)) + + i3 := Resolve(INJ_KEY3) + + r := i3(inj)() + + fmt.Println(r) + + assert.True(t, E.IsRight(r)) + assert.Equal(t, 1, staticCount) + assert.Equal(t, 1, dynamicCount) + assert.Equal(t, 1, lazyEagerCount) +} + +func TestItemProvider(t *testing.T) { + // define a multi token + injMulti := MakeMultiToken[string]("configs") + + // provide some values + v1 := ConstProvider(injMulti.Item(), "Value1") + v2 := ConstProvider(injMulti.Item(), "Value2") + // mix in non-multi values + p1 := ConstProvider(INJ_KEY1, "Value3") + p2 := ConstProvider(INJ_KEY2, "Value4") + + // populate the injector + inj := DIE.MakeInjector(A.From(p1, v1, p2, v2)) + + // access the multi value + multi := Resolve(injMulti.Container()) + + multiInj := multi(inj) + + value := multiInj() + + assert.Equal(t, E.Of[error](A.From("Value1", "Value2")), value) +} + +func TestEmptyItemProvider(t *testing.T) { + // define a multi token + injMulti := MakeMultiToken[string]("configs") + + // mix in non-multi values + p1 := ConstProvider(INJ_KEY1, "Value3") + p2 := ConstProvider(INJ_KEY2, "Value4") + + // populate the injector + inj := DIE.MakeInjector(A.From(p1, p2)) + + // access the multi value + multi := Resolve(injMulti.Container()) + + multiInj := multi(inj) + + value := multiInj() + + assert.Equal(t, E.Of[error](A.Empty[string]()), value) +} + +func TestDependencyOnMultiProvider(t *testing.T) { + // define a multi token + injMulti := MakeMultiToken[string]("configs") + + // provide some values + v1 := ConstProvider(injMulti.Item(), "Value1") + v2 := ConstProvider(injMulti.Item(), "Value2") + // mix in non-multi values + p1 := ConstProvider(INJ_KEY1, "Value3") + p2 := ConstProvider(INJ_KEY2, "Value4") + + fromMulti := func(val string, multi []string) IOE.IOEither[error, string] { + return IOE.Of[error](fmt.Sprintf("Val: %s, Multi: %s", val, multi)) + } + p3 := MakeProvider2(INJ_KEY3, INJ_KEY1.Identity(), injMulti.Container().Identity(), fromMulti) + + // populate the injector + inj := DIE.MakeInjector(A.From(p1, p2, v1, v2, p3)) + + r3 := Resolve(INJ_KEY3) + + v := r3(inj)() + + assert.Equal(t, E.Of[error]("Val: Value3, Multi: [Value1 Value2]"), v) +} + +func TestTokenWithDefaultProvider(t *testing.T) { + // token without a default + injToken1 := MakeToken[string]("Token1") + // token with a default + injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten")) + // dependency + injToken3 := MakeToken[string]("Token3") + + p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOE.IOEither[error, string] { + return IOE.Of[error](fmt.Sprintf("Token: %s", data)) + }) + + // populate the injector + inj := DIE.MakeInjector(A.From(p3)) + + // resolving injToken3 should work and use the default provider for injToken2 + r1 := Resolve(injToken1) + r3 := Resolve(injToken3) + + // inj1 should not be available + assert.True(t, E.IsLeft(r1(inj)())) + // r3 should work + assert.Equal(t, E.Of[error]("Token: Carsten"), r3(inj)()) +} + +func TestTokenWithDefaultProviderAndOverride(t *testing.T) { + // token with a default + injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten")) + // dependency + injToken3 := MakeToken[string]("Token3") + + p2 := ConstProvider(injToken2, "Override") + + p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOE.IOEither[error, string] { + return IOE.Of[error](fmt.Sprintf("Token: %s", data)) + }) + + // populate the injector + inj := DIE.MakeInjector(A.From(p2, p3)) + + // resolving injToken3 should work and use the default provider for injToken2 + r3 := Resolve(injToken3) + + // r3 should work + assert.Equal(t, E.Of[error]("Token: Override"), r3(inj)()) +} diff --git a/v2/di/token.go b/v2/di/token.go new file mode 100644 index 0000000..798f353 --- /dev/null +++ b/v2/di/token.go @@ -0,0 +1,204 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package di + +import ( + "fmt" + "strconv" + "sync/atomic" + + DIE "github.com/IBM/fp-go/v2/di/erasure" + E "github.com/IBM/fp-go/v2/either" + IO "github.com/IBM/fp-go/v2/io" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOO "github.com/IBM/fp-go/v2/iooption" + O "github.com/IBM/fp-go/v2/option" +) + +// Dependency describes the relationship to a service, that has a type and +// a behaviour such as required, option or lazy +type Dependency[T any] interface { + DIE.Dependency + // Unerase converts a value with erased type signature into a strongly typed value + Unerase(val any) E.Either[error, T] +} + +// InjectionToken uniquely identifies a dependency by giving it an Id, Type and name +type InjectionToken[T any] interface { + Dependency[T] + // Identity idenifies this dependency as a mandatory, required dependency, it will be resolved eagerly and injected as `T`. + // If the dependency cannot be resolved, the resolution process fails + Identity() Dependency[T] + // Option identifies this dependency as optional, it will be resolved eagerly and injected as [O.Option[T]]. + // If the dependency cannot be resolved, the resolution process continues and the dependency is represented as [O.None[T]] + Option() Dependency[O.Option[T]] + // IOEither identifies this dependency as mandatory but it will be resolved lazily as a [IOE.IOEither[error, T]]. This + // value is memoized to make sure the dependency is a singleton. + // If the dependency cannot be resolved, the resolution process fails + IOEither() Dependency[IOE.IOEither[error, T]] + // IOOption identifies this dependency as optional but it will be resolved lazily as a [IOO.IOOption[T]]. This + // value is memoized to make sure the dependency is a singleton. + // If the dependency cannot be resolved, the resolution process continues and the dependency is represented as the none value. + IOOption() Dependency[IOO.IOOption[T]] +} + +// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name that can have multiple implementations. +// Implementations are provided via the [MultiInjectionToken.Item] injection token. +type MultiInjectionToken[T any] interface { + // Container returns the injection token used to request an array of all provided items + Container() InjectionToken[[]T] + // Item returns the injection token used to provide an item + Item() InjectionToken[T] +} + +// makeID creates a generator of unique string IDs +func makeID() IO.IO[string] { + var count atomic.Int64 + return IO.MakeIO(func() string { + return strconv.FormatInt(count.Add(1), 16) + }) +} + +// genID is the common generator of unique string IDs +var genID = makeID() + +type tokenBase struct { + name string + id string + flag int + providerFactory O.Option[DIE.ProviderFactory] +} + +type token[T any] struct { + base *tokenBase + toType func(val any) E.Either[error, T] +} + +func (t *token[T]) Id() string { + return t.base.id +} + +func (t *token[T]) Flag() int { + return t.base.flag +} + +func (t *token[T]) String() string { + return t.base.name +} + +func (t *token[T]) Unerase(val any) E.Either[error, T] { + return t.toType(val) +} + +func (t *token[T]) ProviderFactory() O.Option[DIE.ProviderFactory] { + return t.base.providerFactory +} +func makeTokenBase(name string, id string, typ int, providerFactory O.Option[DIE.ProviderFactory]) *tokenBase { + return &tokenBase{name, id, typ, providerFactory} +} + +func makeToken[T any](name string, id string, typ int, unerase func(val any) E.Either[error, T], providerFactory O.Option[DIE.ProviderFactory]) Dependency[T] { + return &token[T]{makeTokenBase(name, id, typ, providerFactory), unerase} +} + +type injectionToken[T any] struct { + token[T] + option Dependency[O.Option[T]] + ioeither Dependency[IOE.IOEither[error, T]] + iooption Dependency[IOO.IOOption[T]] +} + +type multiInjectionToken[T any] struct { + container *injectionToken[[]T] + item *injectionToken[T] +} + +func (i *injectionToken[T]) Identity() Dependency[T] { + return i +} + +func (i *injectionToken[T]) Option() Dependency[O.Option[T]] { + return i.option +} + +func (i *injectionToken[T]) IOEither() Dependency[IOE.IOEither[error, T]] { + return i.ioeither +} + +func (i *injectionToken[T]) IOOption() Dependency[IOO.IOOption[T]] { + return i.iooption +} + +func (i *injectionToken[T]) ProviderFactory() O.Option[DIE.ProviderFactory] { + return i.base.providerFactory +} + +func (m *multiInjectionToken[T]) Container() InjectionToken[[]T] { + return m.container +} + +func (m *multiInjectionToken[T]) Item() InjectionToken[T] { + return m.item +} + +// makeToken create a unique [InjectionToken] for a specific type +func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] { + id := genID() + toIdentity := toType[T]() + return &injectionToken[T]{ + token[T]{makeTokenBase(name, id, DIE.Identity, providerFactory), toIdentity}, + makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", name), id, DIE.Option, toOptionType(toIdentity), providerFactory), + makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", name), id, DIE.IOEither, toIOEitherType(toIdentity), providerFactory), + makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", name), id, DIE.IOOption, toIOOptionType(toIdentity), providerFactory), + } +} + +// MakeToken create a unique [InjectionToken] for a specific type +func MakeToken[T any](name string) InjectionToken[T] { + return makeInjectionToken[T](name, O.None[DIE.ProviderFactory]()) +} + +// MakeToken create a unique [InjectionToken] for a specific type +func MakeTokenWithDefault[T any](name string, providerFactory DIE.ProviderFactory) InjectionToken[T] { + return makeInjectionToken[T](name, O.Of(providerFactory)) +} + +// MakeMultiToken creates a [MultiInjectionToken] +func MakeMultiToken[T any](name string) MultiInjectionToken[T] { + id := genID() + toItem := toType[T]() + toContainer := toArrayType(toItem) + containerName := fmt.Sprintf("Container[%s]", name) + itemName := fmt.Sprintf("Item[%s]", name) + // empty factory + providerFactory := O.None[DIE.ProviderFactory]() + // container + container := &injectionToken[[]T]{ + token[[]T]{makeTokenBase(containerName, id, DIE.Multi|DIE.Identity, providerFactory), toContainer}, + makeToken[O.Option[[]T]](fmt.Sprintf("Option[%s]", containerName), id, DIE.Multi|DIE.Option, toOptionType(toContainer), providerFactory), + makeToken[IOE.IOEither[error, []T]](fmt.Sprintf("IOEither[%s]", containerName), id, DIE.Multi|DIE.IOEither, toIOEitherType(toContainer), providerFactory), + makeToken[IOO.IOOption[[]T]](fmt.Sprintf("IOOption[%s]", containerName), id, DIE.Multi|DIE.IOOption, toIOOptionType(toContainer), providerFactory), + } + // item + item := &injectionToken[T]{ + token[T]{makeTokenBase(itemName, id, DIE.Item|DIE.Identity, providerFactory), toItem}, + makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", itemName), id, DIE.Item|DIE.Option, toOptionType(toItem), providerFactory), + makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", itemName), id, DIE.Item|DIE.IOEither, toIOEitherType(toItem), providerFactory), + makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", itemName), id, DIE.Item|DIE.IOOption, toIOOptionType(toItem), providerFactory), + } + // returns the token + return &multiInjectionToken[T]{container, item} +} diff --git a/v2/di/utils.go b/v2/di/utils.go new file mode 100644 index 0000000..43b1f1a --- /dev/null +++ b/v2/di/utils.go @@ -0,0 +1,84 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package di + +import ( + DIE "github.com/IBM/fp-go/v2/di/erasure" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOO "github.com/IBM/fp-go/v2/iooption" + O "github.com/IBM/fp-go/v2/option" +) + +var ( + toOptionAny = toType[O.Option[any]]() + toIOEitherAny = toType[IOE.IOEither[error, any]]() + toIOOptionAny = toType[IOO.IOOption[any]]() + toArrayAny = toType[[]any]() +) + +// asDependency converts a generic type to a [DIE.Dependency] +func asDependency[T DIE.Dependency](t T) DIE.Dependency { + return t +} + +// toType converts an any to a T +func toType[T any]() func(t any) E.Either[error, T] { + return E.ToType[T](errors.OnSome[any]("Value of type [%T] cannot be converted.")) +} + +// toOptionType converts an any to an Option[any] and then to an Option[T] +func toOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, O.Option[T]] { + return F.Flow2( + toOptionAny, + E.Chain(O.Fold( + F.Nullary2(O.None[T], E.Of[error, O.Option[T]]), + F.Flow2( + item, + E.Map[error](O.Of[T]), + ), + )), + ) +} + +// toIOEitherType converts an any to an IOEither[error, any] and then to an IOEither[error, T] +func toIOEitherType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOE.IOEither[error, T]] { + return F.Flow2( + toIOEitherAny, + E.Map[error](IOE.ChainEitherK(item)), + ) +} + +// toIOOptionType converts an any to an IOOption[any] and then to an IOOption[T] +func toIOOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOO.IOOption[T]] { + return F.Flow2( + toIOOptionAny, + E.Map[error](IOO.ChainOptionK(F.Flow2( + item, + E.ToOption[error, T], + ))), + ) +} + +// toArrayType converts an any to a []T +func toArrayType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, []T] { + return F.Flow2( + toArrayAny, + E.Chain(E.TraverseArray(item)), + ) +} diff --git a/v2/di/utils_test.go b/v2/di/utils_test.go new file mode 100644 index 0000000..58b2d86 --- /dev/null +++ b/v2/di/utils_test.go @@ -0,0 +1,84 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package di + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +var ( + toInt = toType[int]() + toString = toType[string]() +) + +func TestToType(t *testing.T) { + // good cases + assert.Equal(t, E.Of[error](10), toInt(any(10))) + assert.Equal(t, E.Of[error]("Carsten"), toString(any("Carsten"))) + assert.Equal(t, E.Of[error](O.Of("Carsten")), toType[O.Option[string]]()(any(O.Of("Carsten")))) + assert.Equal(t, E.Of[error](O.Of(any("Carsten"))), toType[O.Option[any]]()(any(O.Of(any("Carsten"))))) + // failure + assert.False(t, E.IsRight(toInt(any("Carsten")))) + assert.False(t, E.IsRight(toType[O.Option[string]]()(O.Of(any("Carsten"))))) +} + +func TestToOptionType(t *testing.T) { + // shortcuts + toOptInt := toOptionType(toInt) + toOptString := toOptionType(toString) + // good cases + assert.Equal(t, E.Of[error](O.Of(10)), toOptInt(any(O.Of(any(10))))) + assert.Equal(t, E.Of[error](O.Of("Carsten")), toOptString(any(O.Of(any("Carsten"))))) + // bad cases + assert.False(t, E.IsRight(toOptInt(any(10)))) + assert.False(t, E.IsRight(toOptInt(any(O.Of(10))))) +} + +func invokeIOEither[T any](e E.Either[error, IOE.IOEither[error, T]]) E.Either[error, T] { + return F.Pipe1( + e, + E.Chain(func(ioe IOE.IOEither[error, T]) E.Either[error, T] { + return ioe() + }), + ) +} + +func TestToIOEitherType(t *testing.T) { + // shortcuts + toIOEitherInt := toIOEitherType(toInt) + toIOEitherString := toIOEitherType(toString) + // good cases + assert.Equal(t, E.Of[error](10), invokeIOEither(toIOEitherInt(any(IOE.Of[error](any(10)))))) + assert.Equal(t, E.Of[error]("Carsten"), invokeIOEither(toIOEitherString(any(IOE.Of[error](any("Carsten")))))) + // bad cases + assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any(IOE.Of[error](any(10))))))) + assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any(IOE.Of[error]("Carsten")))))) + assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any("Carsten"))))) +} + +func TestToArrayType(t *testing.T) { + // shortcuts + toArrayString := toArrayType(toString) + // good cases + assert.Equal(t, E.Of[error](A.From("a", "b")), toArrayString(any(A.From(any("a"), any("b"))))) +} diff --git a/v2/either/apply.go b/v2/either/apply.go new file mode 100644 index 0000000..3c9cba0 --- /dev/null +++ b/v2/either/apply.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func ApplySemigroup[E, A any](s S.Semigroup[A]) S.Semigroup[Either[E, A]] { + return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[A, E, A], s) +} + +// ApplicativeMonoid returns a [Monoid] that concatenates [Either] instances via their applicative +func ApplicativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] { + return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[A, E, A], m) +} diff --git a/v2/either/apply_test.go b/v2/either/apply_test.go new file mode 100644 index 0000000..2bbdbf8 --- /dev/null +++ b/v2/either/apply_test.go @@ -0,0 +1,63 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "testing" + + M "github.com/IBM/fp-go/v2/monoid/testing" + N "github.com/IBM/fp-go/v2/number" + "github.com/stretchr/testify/assert" +) + +func TestApplySemigroup(t *testing.T) { + + sg := ApplySemigroup[string](N.SemigroupSum[int]()) + + la := Left[int]("a") + lb := Left[int]("b") + r1 := Right[string](1) + r2 := Right[string](2) + r3 := Right[string](3) + + assert.Equal(t, la, sg.Concat(la, lb)) + assert.Equal(t, lb, sg.Concat(r1, lb)) + assert.Equal(t, la, sg.Concat(la, r2)) + assert.Equal(t, lb, sg.Concat(r1, lb)) + assert.Equal(t, r3, sg.Concat(r1, r2)) +} + +func TestApplicativeMonoid(t *testing.T) { + + m := ApplicativeMonoid[string](N.MonoidSum[int]()) + + la := Left[int]("a") + lb := Left[int]("b") + r1 := Right[string](1) + r2 := Right[string](2) + r3 := Right[string](3) + + assert.Equal(t, la, m.Concat(la, m.Empty())) + assert.Equal(t, lb, m.Concat(m.Empty(), lb)) + assert.Equal(t, r1, m.Concat(r1, m.Empty())) + assert.Equal(t, r2, m.Concat(m.Empty(), r2)) + assert.Equal(t, r3, m.Concat(r1, r2)) +} + +func TestApplicativeMonoidLaws(t *testing.T) { + m := ApplicativeMonoid[string](N.MonoidSum[int]()) + M.AssertLaws(t, m)([]Either[string, int]{Left[int]("a"), Right[string](1)}) +} diff --git a/v2/either/array.go b/v2/either/array.go new file mode 100644 index 0000000..9ecadea --- /dev/null +++ b/v2/either/array.go @@ -0,0 +1,74 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" +) + +// TraverseArrayG transforms an array +func TraverseArrayG[GA ~[]A, GB ~[]B, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] { + return RA.Traverse[GA]( + Of[E, GB], + Map[E, GB, func(B) GB], + Ap[GB, E, B], + + f, + ) +} + +// TraverseArray transforms an array +func TraverseArray[E, A, B any](f func(A) Either[E, B]) func([]A) Either[E, []B] { + return TraverseArrayG[[]A, []B](f) +} + +// TraverseArrayWithIndexG transforms an array +func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, E, A, B any](f func(int, A) Either[E, B]) func(GA) Either[E, GB] { + return RA.TraverseWithIndex[GA]( + Of[E, GB], + Map[E, GB, func(B) GB], + Ap[GB, E, B], + + f, + ) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[E, A, B any](f func(int, A) Either[E, B]) func([]A) Either[E, []B] { + return TraverseArrayWithIndexG[[]A, []B](f) +} + +func SequenceArrayG[GA ~[]A, GOA ~[]Either[E, A], E, A any](ma GOA) Either[E, GA] { + return TraverseArrayG[GOA, GA](F.Identity[Either[E, A]])(ma) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] { + return SequenceArrayG[[]A](ma) +} + +// CompactArrayG discards the none values and keeps the right values +func CompactArrayG[A1 ~[]Either[E, A], A2 ~[]A, E, A any](fa A1) A2 { + return RA.Reduce(fa, func(out A2, value Either[E, A]) A2 { + return MonadFold(value, F.Constant1[E](out), F.Bind1st(RA.Append[A2, A], out)) + }, make(A2, 0, len(fa))) +} + +// CompactArray discards the none values and keeps the right values +func CompactArray[E, A any](fa []Either[E, A]) []A { + return CompactArrayG[[]Either[E, A], []A](fa) +} diff --git a/v2/either/array_test.go b/v2/either/array_test.go new file mode 100644 index 0000000..3d4e8a2 --- /dev/null +++ b/v2/either/array_test.go @@ -0,0 +1,50 @@ +package either + +import ( + "fmt" + "testing" + + TST "github.com/IBM/fp-go/v2/internal/testing" + "github.com/stretchr/testify/assert" +) + +func TestCompactArray(t *testing.T) { + ar := []Either[string, string]{ + Of[string]("ok"), + Left[string]("err"), + Of[string]("ok"), + } + + res := CompactArray(ar) + assert.Equal(t, 2, len(res)) +} + +func TestSequenceArray(t *testing.T) { + + s := TST.SequenceArrayTest( + FromStrictEquals[error, bool](), + Pointed[error, string](), + Pointed[error, bool](), + Functor[error, []string, bool](), + SequenceArray[error, string], + ) + + for i := 0; i < 10; i++ { + t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i)) + } +} + +func TestSequenceArrayError(t *testing.T) { + + s := TST.SequenceArrayErrorTest( + FromStrictEquals[error, bool](), + Left[string, error], + Left[bool, error], + Pointed[error, string](), + Pointed[error, bool](), + Functor[error, []string, bool](), + SequenceArray[error, string], + ) + // run across four bits + s(4)(t) +} diff --git a/v2/either/bind.go b/v2/either/bind.go new file mode 100644 index 0000000..37b3841 --- /dev/null +++ b/v2/either/bind.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[E, S any]( + empty S, +) Either[E, S] { + return Of[E](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) Either[E, T], +) func(Either[E, S1]) Either[E, S2] { + return C.Bind( + Chain[E, S1, S2], + Map[E, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[E, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(Either[E, S1]) Either[E, S2] { + return F.Let( + Map[E, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[E, S1, S2, T any]( + key func(T) func(S1) S2, + b T, +) func(Either[E, S1]) Either[E, S2] { + return F.LetTo( + Map[E, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[E, S1, T any]( + setter func(T) S1, +) func(Either[E, T]) Either[E, S1] { + return C.BindTo( + Map[E, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa Either[E, T], +) func(Either[E, S1]) Either[E, S2] { + return A.ApS( + Ap[S2, E, T], + Map[E, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/either/bind_test.go b/v2/either/bind_test.go new file mode 100644 index 0000000..4ad65aa --- /dev/null +++ b/v2/either/bind_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) Either[error, string] { + return Of[error]("Doe") +} + +func getGivenName(s utils.WithLastName) Either[error, string] { + return Of[error]("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do[error](utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map[error](utils.GetFullName), + ) + + assert.Equal(t, res, Of[error]("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do[error](utils.Empty), + ApS(utils.SetLastName, Of[error]("Doe")), + ApS(utils.SetGivenName, Of[error]("John")), + Map[error](utils.GetFullName), + ) + + assert.Equal(t, res, Of[error]("John Doe")) +} diff --git a/v2/either/core.go b/v2/either/core.go new file mode 100644 index 0000000..b9e40ae --- /dev/null +++ b/v2/either/core.go @@ -0,0 +1,101 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "fmt" +) + +type ( + either struct { + isLeft bool + value any + } + + // Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases + Either[E, A any] either +) + +// String prints some debug info for the object +// +// go:noinline +func eitherString(s *either) string { + if s.isLeft { + return fmt.Sprintf("Left[%T](%v)", s.value, s.value) + } + return fmt.Sprintf("Right[%T](%v)", s.value, s.value) +} + +// Format prints some debug info for the object +// +// go:noinline +func eitherFormat(e *either, f fmt.State, c rune) { + switch c { + case 's': + fmt.Fprint(f, eitherString(e)) + default: + fmt.Fprint(f, eitherString(e)) + } +} + +// String prints some debug info for the object +func (s Either[E, A]) String() string { + return eitherString((*either)(&s)) +} + +// Format prints some debug info for the object +func (s Either[E, A]) Format(f fmt.State, c rune) { + eitherFormat((*either)(&s), f, c) +} + +// IsLeft tests if the [Either] is a left value. Rather use [Fold] if you need to access the values. Inverse is [IsRight]. +func IsLeft[E, A any](val Either[E, A]) bool { + return val.isLeft +} + +// IsLeft tests if the [Either] is a right value. Rather use [Fold] if you need to access the values. Inverse is [IsLeft]. +func IsRight[E, A any](val Either[E, A]) bool { + return !val.isLeft +} + +// Left creates a new instance of an [Either] representing the left value. +func Left[A, E any](value E) Either[E, A] { + return Either[E, A]{true, value} +} + +// Right creates a new instance of an [Either] representing the right value. +func Right[E, A any](value A) Either[E, A] { + return Either[E, A]{false, value} +} + +// MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case +func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B { + if ma.isLeft { + return onLeft(ma.value.(E)) + } + return onRight(ma.value.(A)) +} + +// Unwrap converts an [Either] into the idiomatic tuple +func Unwrap[E, A any](ma Either[E, A]) (A, E) { + if ma.isLeft { + var a A + return a, ma.value.(E) + } else { + var e E + return ma.value.(A), e + } +} diff --git a/v2/either/curry.go b/v2/either/curry.go new file mode 100644 index 0000000..d7570b6 --- /dev/null +++ b/v2/either/curry.go @@ -0,0 +1,76 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// these function curry a golang function that returns an error into its curried version that returns an either + +func Curry0[R any](f func() (R, error)) func() Either[error, R] { + return Eitherize0(f) +} + +func Curry1[T1, R any](f func(T1) (R, error)) func(T1) Either[error, R] { + return Eitherize1(f) +} + +func Curry2[T1, T2, R any](f func(T1, T2) (R, error)) func(T1) func(T2) Either[error, R] { + return F.Curry2(Eitherize2(f)) +} + +func Curry3[T1, T2, T3, R any](f func(T1, T2, T3) (R, error)) func(T1) func(T2) func(T3) Either[error, R] { + return F.Curry3(Eitherize3(f)) +} + +func Curry4[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) (R, error)) func(T1) func(T2) func(T3) func(T4) Either[error, R] { + return F.Curry4(Eitherize4(f)) +} + +func Uncurry0[R any](f func() Either[error, R]) func() (R, error) { + return func() (R, error) { + return UnwrapError(f()) + } +} + +func Uncurry1[T1, R any](f func(T1) Either[error, R]) func(T1) (R, error) { + uc := F.Uncurry1(f) + return func(t1 T1) (R, error) { + return UnwrapError(uc(t1)) + } +} + +func Uncurry2[T1, T2, R any](f func(T1) func(T2) Either[error, R]) func(T1, T2) (R, error) { + uc := F.Uncurry2(f) + return func(t1 T1, t2 T2) (R, error) { + return UnwrapError(uc(t1, t2)) + } +} + +func Uncurry3[T1, T2, T3, R any](f func(T1) func(T2) func(T3) Either[error, R]) func(T1, T2, T3) (R, error) { + uc := F.Uncurry3(f) + return func(t1 T1, t2 T2, t3 T3) (R, error) { + return UnwrapError(uc(t1, t2, t3)) + } +} + +func Uncurry4[T1, T2, T3, T4, R any](f func(T1) func(T2) func(T3) func(T4) Either[error, R]) func(T1, T2, T3, T4) (R, error) { + uc := F.Uncurry4(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error) { + return UnwrapError(uc(t1, t2, t3, t4)) + } +} diff --git a/v2/either/doc.go b/v2/either/doc.go new file mode 100644 index 0000000..17a4f26 --- /dev/null +++ b/v2/either/doc.go @@ -0,0 +1,19 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package option defines the [Either] datastructure and its monadic operations +package either + +//go:generate go run .. either --count 15 --filename gen.go diff --git a/v2/either/either.go b/v2/either/either.go new file mode 100644 index 0000000..6f947ae --- /dev/null +++ b/v2/either/either.go @@ -0,0 +1,267 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// package either implements the Either monad +// +// A data type that can be of either of two types but not both. This is +// typically used to carry an error or a return value +package either + +import ( + E "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + FC "github.com/IBM/fp-go/v2/internal/functor" + L "github.com/IBM/fp-go/v2/lazy" + O "github.com/IBM/fp-go/v2/option" +) + +// Of is equivalent to [Right] +func Of[E, A any](value A) Either[E, A] { + return F.Pipe1(value, Right[E, A]) +} + +func FromIO[E any, IO ~func() A, A any](f IO) Either[E, A] { + return F.Pipe1(f(), Right[E, A]) +} + +func MonadAp[B, E, A any](fab Either[E, func(a A) B], fa Either[E, A]) Either[E, B] { + return MonadFold(fab, Left[B, E], func(ab func(A) B) Either[E, B] { + return MonadFold(fa, Left[B, E], F.Flow2(ab, Right[E, B])) + }) +} + +func Ap[B, E, A any](fa Either[E, A]) func(fab Either[E, func(a A) B]) Either[E, B] { + return F.Bind2nd(MonadAp[B, E, A], fa) +} + +func MonadMap[E, A, B any](fa Either[E, A], f func(a A) B) Either[E, B] { + return MonadChain(fa, F.Flow2(f, Right[E, B])) +} + +func MonadBiMap[E1, E2, A, B any](fa Either[E1, A], f func(E1) E2, g func(a A) B) Either[E2, B] { + return MonadFold(fa, F.Flow2(f, Left[B, E2]), F.Flow2(g, Right[E2, B])) +} + +// BiMap maps a pair of functions over the two type arguments of the bifunctor. +func BiMap[E1, E2, A, B any](f func(E1) E2, g func(a A) B) func(Either[E1, A]) Either[E2, B] { + return Fold(F.Flow2(f, Left[B, E2]), F.Flow2(g, Right[E2, B])) +} + +func MonadMapTo[E, A, B any](fa Either[E, A], b B) Either[E, B] { + return MonadMap(fa, F.Constant1[A](b)) +} + +func MapTo[E, A, B any](b B) func(Either[E, A]) Either[E, B] { + return Map[E](F.Constant1[A](b)) +} + +func MonadMapLeft[E1, A, E2 any](fa Either[E1, A], f func(E1) E2) Either[E2, A] { + return MonadFold(fa, F.Flow2(f, Left[A, E2]), Right[E2, A]) +} + +func Map[E, A, B any](f func(a A) B) func(fa Either[E, A]) Either[E, B] { + return Chain(F.Flow2(f, Right[E, B])) +} + +// MapLeft applies a mapping function to the error channel +func MapLeft[A, E1, E2 any](f func(E1) E2) func(fa Either[E1, A]) Either[E2, A] { + return Fold(F.Flow2(f, Left[A, E2]), Right[E2, A]) +} + +func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] { + return MonadFold(fa, Left[B, E], f) +} + +func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] { + return C.MonadChainFirst( + MonadChain[E, A, A], + MonadMap[E, B, A], + ma, + f, + ) +} + +func MonadChainTo[A, E, B any](_ Either[E, A], mb Either[E, B]) Either[E, B] { + return mb +} + +func MonadChainOptionK[A, B, E any](onNone func() E, ma Either[E, A], f func(A) O.Option[B]) Either[E, B] { + return MonadChain(ma, F.Flow2(f, FromOption[B](onNone))) +} + +func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(Either[E, A]) Either[E, B] { + from := FromOption[B](onNone) + return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] { + return Chain(F.Flow2(f, from)) + } +} + +func ChainTo[A, E, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] { + return F.Constant1[Either[E, A]](mb) +} + +func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] { + return Fold(Left[B, E], f) +} + +func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] { + return C.ChainFirst( + Chain[E, A, A], + Map[E, B, A], + f, + ) +} + +func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] { + return MonadChain(mma, F.Identity[Either[E, A]]) +} + +func TryCatch[FE func(error) E, E, A any](val A, err error, onThrow FE) Either[E, A] { + if err != nil { + return F.Pipe2(err, onThrow, Left[A, E]) + } + return F.Pipe1(val, Right[E, A]) +} + +func TryCatchError[A any](val A, err error) Either[error, A] { + return TryCatch(val, err, E.IdentityError) +} + +func Sequence2[E, T1, T2, R any](f func(T1, T2) Either[E, R]) func(Either[E, T1], Either[E, T2]) Either[E, R] { + return func(e1 Either[E, T1], e2 Either[E, T2]) Either[E, R] { + return MonadSequence2(e1, e2, f) + } +} + +func Sequence3[E, T1, T2, T3, R any](f func(T1, T2, T3) Either[E, R]) func(Either[E, T1], Either[E, T2], Either[E, T3]) Either[E, R] { + return func(e1 Either[E, T1], e2 Either[E, T2], e3 Either[E, T3]) Either[E, R] { + return MonadSequence3(e1, e2, e3, f) + } +} + +func FromOption[A, E any](onNone func() E) func(O.Option[A]) Either[E, A] { + return O.Fold(F.Nullary2(onNone, Left[A, E]), Right[E, A]) +} + +func ToOption[E, A any](ma Either[E, A]) O.Option[A] { + return MonadFold(ma, F.Ignore1of1[E](O.None[A]), O.Some[A]) +} + +func FromError[A any](f func(a A) error) func(A) Either[error, A] { + return func(a A) Either[error, A] { + return TryCatchError(a, f(a)) + } +} + +func ToError[A any](e Either[error, A]) error { + return MonadFold(e, E.IdentityError, F.Constant1[A, error](nil)) +} + +func Fold[E, A, B any](onLeft func(E) B, onRight func(A) B) func(Either[E, A]) B { + return func(ma Either[E, A]) B { + return MonadFold(ma, onLeft, onRight) + } +} + +// UnwrapError converts an Either into the idiomatic tuple +func UnwrapError[A any](ma Either[error, A]) (A, error) { + return Unwrap[error](ma) +} + +func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Either[E, A] { + return func(a A) Either[E, A] { + if pred(a) { + return Right[E](a) + } + return Left[A, E](onFalse(a)) + } +} + +func FromNillable[A, E any](e E) func(*A) Either[E, *A] { + return FromPredicate(F.IsNonNil[A], F.Constant1[*A](e)) +} + +func GetOrElse[E, A any](onLeft func(E) A) func(Either[E, A]) A { + return Fold(onLeft, F.Identity[A]) +} + +func Reduce[E, A, B any](f func(B, A) B, initial B) func(Either[E, A]) B { + return Fold( + F.Constant1[E](initial), + F.Bind1st(f, initial), + ) +} + +func AltW[E, E1, A any](that L.Lazy[Either[E1, A]]) func(Either[E, A]) Either[E1, A] { + return Fold(F.Ignore1of1[E](that), Right[E1, A]) +} + +func Alt[E, A any](that L.Lazy[Either[E, A]]) func(Either[E, A]) Either[E, A] { + return AltW[E](that) +} + +func OrElse[E, A any](onLeft func(e E) Either[E, A]) func(Either[E, A]) Either[E, A] { + return Fold(onLeft, Of[E, A]) +} + +func ToType[A, E any](onError func(any) E) func(any) Either[E, A] { + return func(value any) Either[E, A] { + return F.Pipe2( + value, + O.ToType[A], + O.Fold(F.Nullary3(F.Constant(value), onError, Left[A, E]), Right[E, A]), + ) + } +} + +func Memoize[E, A any](val Either[E, A]) Either[E, A] { + return val +} + +func MonadSequence2[E, T1, T2, R any](e1 Either[E, T1], e2 Either[E, T2], f func(T1, T2) Either[E, R]) Either[E, R] { + return MonadFold(e1, Left[R, E], func(t1 T1) Either[E, R] { + return MonadFold(e2, Left[R, E], func(t2 T2) Either[E, R] { + return f(t1, t2) + }) + }) +} + +func MonadSequence3[E, T1, T2, T3, R any](e1 Either[E, T1], e2 Either[E, T2], e3 Either[E, T3], f func(T1, T2, T3) Either[E, R]) Either[E, R] { + return MonadFold(e1, Left[R, E], func(t1 T1) Either[E, R] { + return MonadFold(e2, Left[R, E], func(t2 T2) Either[E, R] { + return MonadFold(e3, Left[R, E], func(t3 T3) Either[E, R] { + return f(t1, t2, t3) + }) + }) + }) +} + +// Swap changes the order of type parameters +func Swap[E, A any](val Either[E, A]) Either[A, E] { + return MonadFold(val, Right[A, E], Left[E, A]) +} + +func MonadFlap[E, B, A any](fab Either[E, func(A) B], a A) Either[E, B] { + return FC.MonadFlap(MonadMap[E, func(A) B, B], fab, a) +} + +func Flap[E, B, A any](a A) func(Either[E, func(A) B]) Either[E, B] { + return FC.Flap(Map[E, func(A) B, B], a) +} + +func MonadAlt[E, A any](fa Either[E, A], that L.Lazy[Either[E, A]]) Either[E, A] { + return MonadFold(fa, F.Ignore1of1[E](that), Of[E, A]) +} diff --git a/v2/either/either_test.go b/v2/either/either_test.go new file mode 100644 index 0000000..35ea037 --- /dev/null +++ b/v2/either/either_test.go @@ -0,0 +1,129 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "errors" + "fmt" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + IO "github.com/IBM/fp-go/v2/io" + O "github.com/IBM/fp-go/v2/option" + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +func TestIsLeft(t *testing.T) { + err := errors.New("Some error") + withError := Left[string](err) + + assert.True(t, IsLeft(withError)) + assert.False(t, IsRight(withError)) +} + +func TestIsRight(t *testing.T) { + noError := Right[error]("Carsten") + + assert.True(t, IsRight(noError)) + assert.False(t, IsLeft(noError)) +} + +func TestMapEither(t *testing.T) { + + assert.Equal(t, F.Pipe1(Right[error]("abc"), Map[error](utils.StringLen)), Right[error](3)) + + val2 := F.Pipe1(Left[string, string]("s"), Map[string](utils.StringLen)) + exp2 := Left[int]("s") + + assert.Equal(t, val2, exp2) +} + +func TestUnwrapError(t *testing.T) { + a := "" + err := errors.New("Some error") + withError := Left[string](err) + + res, extracted := UnwrapError(withError) + assert.Equal(t, a, res) + assert.Equal(t, extracted, err) + +} + +func TestReduce(t *testing.T) { + + s := S.Semigroup() + + assert.Equal(t, "foobar", F.Pipe1(Right[string]("bar"), Reduce[string](s.Concat, "foo"))) + assert.Equal(t, "foo", F.Pipe1(Left[string, string]("bar"), Reduce[string](s.Concat, "foo"))) + +} +func TestAp(t *testing.T) { + f := S.Size + + assert.Equal(t, Right[string](3), F.Pipe1(Right[string](f), Ap[int, string, string](Right[string]("abc")))) + assert.Equal(t, Left[int]("maError"), F.Pipe1(Right[string](f), Ap[int, string, string](Left[string, string]("maError")))) + assert.Equal(t, Left[int]("mabError"), F.Pipe1(Left[func(string) int]("mabError"), Ap[int, string, string](Left[string, string]("maError")))) +} + +func TestAlt(t *testing.T) { + assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Right[string](2))))) + assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Left[int]("a"))))) + assert.Equal(t, Right[string](2), F.Pipe1(Left[int]("b"), Alt(F.Constant(Right[string](2))))) + assert.Equal(t, Left[int]("b"), F.Pipe1(Left[int]("a"), Alt(F.Constant(Left[int]("b"))))) +} + +func TestChainFirst(t *testing.T) { + f := F.Flow2(S.Size, Right[string, int]) + + assert.Equal(t, Right[string]("abc"), F.Pipe1(Right[string]("abc"), ChainFirst(f))) + assert.Equal(t, Left[string, string]("maError"), F.Pipe1(Left[string, string]("maError"), ChainFirst(f))) +} + +func TestChainOptionK(t *testing.T) { + f := ChainOptionK[int, int](F.Constant("a"))(func(n int) O.Option[int] { + if n > 0 { + return O.Some(n) + } + return O.None[int]() + }) + assert.Equal(t, Right[string](1), f(Right[string](1))) + assert.Equal(t, Left[int]("a"), f(Right[string](-1))) + assert.Equal(t, Left[int]("b"), f(Left[int]("b"))) +} + +func TestFromOption(t *testing.T) { + assert.Equal(t, Left[int]("none"), FromOption[int](F.Constant("none"))(O.None[int]())) + assert.Equal(t, Right[string](1), FromOption[int](F.Constant("none"))(O.Some(1))) +} + +func TestStringer(t *testing.T) { + e := Of[error]("foo") + exp := "Right[string](foo)" + + assert.Equal(t, exp, e.String()) + + var s fmt.Stringer = e + assert.Equal(t, exp, s.String()) +} + +func TestFromIO(t *testing.T) { + f := IO.Of("abc") + e := FromIO[error](f) + + assert.Equal(t, Right[error]("abc"), e) +} diff --git a/v2/either/eq.go b/v2/either/eq.go new file mode 100644 index 0000000..e250f69 --- /dev/null +++ b/v2/either/eq.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" +) + +// Constructs an equal predicate for an `Either` +func Eq[E, A any](e EQ.Eq[E], a EQ.Eq[A]) EQ.Eq[Either[E, A]] { + // some convenient shortcuts + eqa := F.Curry2(a.Equals) + eqe := F.Curry2(e.Equals) + + fca := F.Bind2nd(Fold[E, A, bool], F.Constant1[A](false)) + fce := F.Bind1st(Fold[E, A, bool], F.Constant1[E](false)) + + fld := Fold(F.Flow2(eqe, fca), F.Flow2(eqa, fce)) + + return EQ.FromEquals(F.Uncurry2(fld)) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[E, A comparable]() EQ.Eq[Either[E, A]] { + return Eq(EQ.FromStrictEquals[E](), EQ.FromStrictEquals[A]()) +} diff --git a/v2/either/eq_test.go b/v2/either/eq_test.go new file mode 100644 index 0000000..7ccee69 --- /dev/null +++ b/v2/either/eq_test.go @@ -0,0 +1,45 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEq(t *testing.T) { + + r1 := Of[string](1) + r2 := Of[string](1) + r3 := Of[string](2) + + e1 := Left[int]("a") + e2 := Left[int]("a") + e3 := Left[int]("b") + + eq := FromStrictEquals[string, int]() + + assert.True(t, eq.Equals(r1, r1)) + assert.True(t, eq.Equals(r1, r2)) + assert.False(t, eq.Equals(r1, r3)) + assert.False(t, eq.Equals(r1, e1)) + + assert.True(t, eq.Equals(e1, e1)) + assert.True(t, eq.Equals(e1, e2)) + assert.False(t, eq.Equals(e1, e3)) + assert.False(t, eq.Equals(e2, r2)) +} diff --git a/v2/either/examples_create_test.go b/v2/either/examples_create_test.go new file mode 100644 index 0000000..74c0ecc --- /dev/null +++ b/v2/either/examples_create_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "fmt" + + "github.com/IBM/fp-go/v2/errors" +) + +func ExampleEither_creation() { + // Build an Either + leftValue := Left[string](fmt.Errorf("some error")) + rightValue := Right[error]("value") + + // Build from a value + fromNillable := FromNillable[string](fmt.Errorf("value was nil")) + leftFromNil := fromNillable(nil) + value := "value" + rightFromPointer := fromNillable(&value) + + // some predicate + isEven := func(num int) bool { + return num%2 == 0 + } + fromEven := FromPredicate(isEven, errors.OnSome[int]("%d is an odd number")) + leftFromPred := fromEven(3) + rightFromPred := fromEven(4) + + fmt.Println(leftValue) + fmt.Println(rightValue) + fmt.Println(leftFromNil) + fmt.Println(IsRight(rightFromPointer)) + fmt.Println(leftFromPred) + fmt.Println(rightFromPred) + + // Output: + // Left[*errors.errorString](some error) + // Right[string](value) + // Left[*errors.errorString](value was nil) + // true + // Left[*errors.errorString](3 is an odd number) + // Right[int](4) + +} diff --git a/v2/either/examples_extract_test.go b/v2/either/examples_extract_test.go new file mode 100644 index 0000000..0ec619f --- /dev/null +++ b/v2/either/examples_extract_test.go @@ -0,0 +1,64 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" +) + +func ExampleEither_extraction() { + leftValue := Left[int](fmt.Errorf("Division by Zero!")) + rightValue := Right[error](10) + + // Convert Either[E, A] to A with a default value + leftWithDefault := GetOrElse(F.Constant1[error](0))(leftValue) // 0 + rightWithDefault := GetOrElse(F.Constant1[error](0))(rightValue) // 10 + + // Apply a different function on Left(...)/Right(...) + doubleOrZero := Fold(F.Constant1[error](0), N.Mul(2)) // func(Either[error, int]) int + doubleFromLeft := doubleOrZero(leftValue) // 0 + doubleFromRight := doubleOrZero(rightValue) // 20 + + // Pro-tip: Fold is short for the following: + doubleOrZeroBis := F.Flow2( + Map[error](N.Mul(2)), + GetOrElse(F.Constant1[error](0)), + ) + doubleFromLeftBis := doubleOrZeroBis(leftValue) // 0 + doubleFromRightBis := doubleOrZeroBis(rightValue) // 20 + + fmt.Println(leftValue) + fmt.Println(rightValue) + fmt.Println(leftWithDefault) + fmt.Println(rightWithDefault) + fmt.Println(doubleFromLeft) + fmt.Println(doubleFromRight) + fmt.Println(doubleFromLeftBis) + fmt.Println(doubleFromRightBis) + + // Output: + // Left[*errors.errorString](Division by Zero!) + // Right[int](10) + // 0 + // 10 + // 0 + // 20 + // 0 + // 20 +} diff --git a/v2/either/exec/exec.go b/v2/either/exec/exec.go new file mode 100644 index 0000000..56837da --- /dev/null +++ b/v2/either/exec/exec.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package exec + +import ( + "context" + + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/exec" + F "github.com/IBM/fp-go/v2/function" + GE "github.com/IBM/fp-go/v2/internal/exec" +) + +var ( + // Command executes a command + // use this version if the command does not produce any side effect, i.e. if the output is uniquely determined by by the input + // typically you'd rather use the [IOEither] version of the command + Command = F.Curry3(command) +) + +func command(name string, args []string, in []byte) E.Either[error, exec.CommandOutput] { + return E.TryCatchError(GE.Exec(context.Background(), name, args, in)) +} diff --git a/v2/either/functor.go b/v2/either/functor.go new file mode 100644 index 0000000..c862b54 --- /dev/null +++ b/v2/either/functor.go @@ -0,0 +1,31 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "github.com/IBM/fp-go/v2/internal/functor" +) + +type eitherFunctor[E, A, B any] struct{} + +func (o *eitherFunctor[E, A, B]) Map(f func(A) B) func(Either[E, A]) Either[E, B] { + return Map[E, A, B](f) +} + +// Functor implements the functoric operations for [Either] +func Functor[E, A, B any]() functor.Functor[A, B, Either[E, A], Either[E, B]] { + return &eitherFunctor[E, A, B]{} +} diff --git a/v2/either/gen.go b/v2/either/gen.go new file mode 100644 index 0000000..4626d64 --- /dev/null +++ b/v2/either/gen.go @@ -0,0 +1,1196 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:18:50.8721435 +0100 CET m=+0.070394501 + +package either + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// Eitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning an Either +// The inverse function is [Uneitherize0] +func Eitherize0[F ~func() (R, error), R any](f F) func() Either[error, R] { + return func() Either[error, R] { + return TryCatchError(f()) + } +} + +// Uneitherize0 converts a function with 0 parameters returning an Either into a function with 0 parameters returning a tuple +// The inverse function is [Eitherize0] +func Uneitherize0[F ~func() Either[error, R], R any](f F) func() (R, error) { + return func() (R, error) { + return UnwrapError(f()) + } +} + +// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning an Either +// The inverse function is [Uneitherize1] +func Eitherize1[F ~func(T0) (R, error), T0, R any](f F) func(T0) Either[error, R] { + return func(t0 T0) Either[error, R] { + return TryCatchError(f(t0)) + } +} + +// Uneitherize1 converts a function with 1 parameters returning an Either into a function with 1 parameters returning a tuple +// The inverse function is [Eitherize1] +func Uneitherize1[F ~func(T0) Either[error, R], T0, R any](f F) func(T0) (R, error) { + return func(t0 T0) (R, error) { + return UnwrapError(f(t0)) + } +} + +// SequenceT1 converts 1 parameters of [Either[E, T]] into a [Either[E, Tuple1]]. +func SequenceT1[E, T1 any](t1 Either[E, T1]) Either[E, T.Tuple1[T1]] { + return A.SequenceT1( + Map[E, T1, T.Tuple1[T1]], + t1, + ) +} + +// SequenceTuple1 converts a [Tuple1] of [Either[E, T]] into an [Either[E, Tuple1]]. +func SequenceTuple1[E, T1 any](t T.Tuple1[Either[E, T1]]) Either[E, T.Tuple1[T1]] { + return A.SequenceTuple1( + Map[E, T1, T.Tuple1[T1]], + t, + ) +} + +// TraverseTuple1 converts a [Tuple1] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple1]]. +func TraverseTuple1[F1 ~func(A1) Either[E, T1], E, A1, T1 any](f1 F1) func(T.Tuple1[A1]) Either[E, T.Tuple1[T1]] { + return func(t T.Tuple1[A1]) Either[E, T.Tuple1[T1]] { + return A.TraverseTuple1( + Map[E, T1, T.Tuple1[T1]], + f1, + t, + ) + } +} + +// Eitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning an Either +// The inverse function is [Uneitherize2] +func Eitherize2[F ~func(T0, T1) (R, error), T0, T1, R any](f F) func(T0, T1) Either[error, R] { + return func(t0 T0, t1 T1) Either[error, R] { + return TryCatchError(f(t0, t1)) + } +} + +// Uneitherize2 converts a function with 2 parameters returning an Either into a function with 2 parameters returning a tuple +// The inverse function is [Eitherize2] +func Uneitherize2[F ~func(T0, T1) Either[error, R], T0, T1, R any](f F) func(T0, T1) (R, error) { + return func(t0 T0, t1 T1) (R, error) { + return UnwrapError(f(t0, t1)) + } +} + +// SequenceT2 converts 2 parameters of [Either[E, T]] into a [Either[E, Tuple2]]. +func SequenceT2[E, T1, T2 any](t1 Either[E, T1], t2 Either[E, T2]) Either[E, T.Tuple2[T1, T2]] { + return A.SequenceT2( + Map[E, T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], E, T2], + t1, + t2, + ) +} + +// SequenceTuple2 converts a [Tuple2] of [Either[E, T]] into an [Either[E, Tuple2]]. +func SequenceTuple2[E, T1, T2 any](t T.Tuple2[Either[E, T1], Either[E, T2]]) Either[E, T.Tuple2[T1, T2]] { + return A.SequenceTuple2( + Map[E, T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], E, T2], + t, + ) +} + +// TraverseTuple2 converts a [Tuple2] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple2]]. +func TraverseTuple2[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], E, A1, T1, A2, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) Either[E, T.Tuple2[T1, T2]] { + return func(t T.Tuple2[A1, A2]) Either[E, T.Tuple2[T1, T2]] { + return A.TraverseTuple2( + Map[E, T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], E, T2], + f1, + f2, + t, + ) + } +} + +// Eitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning an Either +// The inverse function is [Uneitherize3] +func Eitherize3[F ~func(T0, T1, T2) (R, error), T0, T1, T2, R any](f F) func(T0, T1, T2) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2) Either[error, R] { + return TryCatchError(f(t0, t1, t2)) + } +} + +// Uneitherize3 converts a function with 3 parameters returning an Either into a function with 3 parameters returning a tuple +// The inverse function is [Eitherize3] +func Uneitherize3[F ~func(T0, T1, T2) Either[error, R], T0, T1, T2, R any](f F) func(T0, T1, T2) (R, error) { + return func(t0 T0, t1 T1, t2 T2) (R, error) { + return UnwrapError(f(t0, t1, t2)) + } +} + +// SequenceT3 converts 3 parameters of [Either[E, T]] into a [Either[E, Tuple3]]. +func SequenceT3[E, T1, T2, T3 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3]) Either[E, T.Tuple3[T1, T2, T3]] { + return A.SequenceT3( + Map[E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], E, T2], + Ap[T.Tuple3[T1, T2, T3], E, T3], + t1, + t2, + t3, + ) +} + +// SequenceTuple3 converts a [Tuple3] of [Either[E, T]] into an [Either[E, Tuple3]]. +func SequenceTuple3[E, T1, T2, T3 any](t T.Tuple3[Either[E, T1], Either[E, T2], Either[E, T3]]) Either[E, T.Tuple3[T1, T2, T3]] { + return A.SequenceTuple3( + Map[E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], E, T2], + Ap[T.Tuple3[T1, T2, T3], E, T3], + t, + ) +} + +// TraverseTuple3 converts a [Tuple3] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple3]]. +func TraverseTuple3[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], E, A1, T1, A2, T2, A3, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) Either[E, T.Tuple3[T1, T2, T3]] { + return func(t T.Tuple3[A1, A2, A3]) Either[E, T.Tuple3[T1, T2, T3]] { + return A.TraverseTuple3( + Map[E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], E, T2], + Ap[T.Tuple3[T1, T2, T3], E, T3], + f1, + f2, + f3, + t, + ) + } +} + +// Eitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning an Either +// The inverse function is [Uneitherize4] +func Eitherize4[F ~func(T0, T1, T2, T3) (R, error), T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3)) + } +} + +// Uneitherize4 converts a function with 4 parameters returning an Either into a function with 4 parameters returning a tuple +// The inverse function is [Eitherize4] +func Uneitherize4[F ~func(T0, T1, T2, T3) Either[error, R], T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3) (R, error) { + return UnwrapError(f(t0, t1, t2, t3)) + } +} + +// SequenceT4 converts 4 parameters of [Either[E, T]] into a [Either[E, Tuple4]]. +func SequenceT4[E, T1, T2, T3, T4 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4]) Either[E, T.Tuple4[T1, T2, T3, T4]] { + return A.SequenceT4( + Map[E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], E, T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], E, T3], + Ap[T.Tuple4[T1, T2, T3, T4], E, T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceTuple4 converts a [Tuple4] of [Either[E, T]] into an [Either[E, Tuple4]]. +func SequenceTuple4[E, T1, T2, T3, T4 any](t T.Tuple4[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4]]) Either[E, T.Tuple4[T1, T2, T3, T4]] { + return A.SequenceTuple4( + Map[E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], E, T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], E, T3], + Ap[T.Tuple4[T1, T2, T3, T4], E, T4], + t, + ) +} + +// TraverseTuple4 converts a [Tuple4] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple4]]. +func TraverseTuple4[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], E, A1, T1, A2, T2, A3, T3, A4, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) Either[E, T.Tuple4[T1, T2, T3, T4]] { + return func(t T.Tuple4[A1, A2, A3, A4]) Either[E, T.Tuple4[T1, T2, T3, T4]] { + return A.TraverseTuple4( + Map[E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], E, T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], E, T3], + Ap[T.Tuple4[T1, T2, T3, T4], E, T4], + f1, + f2, + f3, + f4, + t, + ) + } +} + +// Eitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning an Either +// The inverse function is [Uneitherize5] +func Eitherize5[F ~func(T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4)) + } +} + +// Uneitherize5 converts a function with 5 parameters returning an Either into a function with 5 parameters returning a tuple +// The inverse function is [Eitherize5] +func Uneitherize5[F ~func(T0, T1, T2, T3, T4) Either[error, R], T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4)) + } +} + +// SequenceT5 converts 5 parameters of [Either[E, T]] into a [Either[E, Tuple5]]. +func SequenceT5[E, T1, T2, T3, T4, T5 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5]) Either[E, T.Tuple5[T1, T2, T3, T4, T5]] { + return A.SequenceT5( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], E, T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceTuple5 converts a [Tuple5] of [Either[E, T]] into an [Either[E, Tuple5]]. +func SequenceTuple5[E, T1, T2, T3, T4, T5 any](t T.Tuple5[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5]]) Either[E, T.Tuple5[T1, T2, T3, T4, T5]] { + return A.SequenceTuple5( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], E, T5], + t, + ) +} + +// TraverseTuple5 converts a [Tuple5] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple5]]. +func TraverseTuple5[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) Either[E, T.Tuple5[T1, T2, T3, T4, T5]] { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) Either[E, T.Tuple5[T1, T2, T3, T4, T5]] { + return A.TraverseTuple5( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], E, T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], E, T5], + f1, + f2, + f3, + f4, + f5, + t, + ) + } +} + +// Eitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning an Either +// The inverse function is [Uneitherize6] +func Eitherize6[F ~func(T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5)) + } +} + +// Uneitherize6 converts a function with 6 parameters returning an Either into a function with 6 parameters returning a tuple +// The inverse function is [Eitherize6] +func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) Either[error, R], T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5)) + } +} + +// SequenceT6 converts 6 parameters of [Either[E, T]] into a [Either[E, Tuple6]]. +func SequenceT6[E, T1, T2, T3, T4, T5, T6 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6]) Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return A.SequenceT6( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], E, T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceTuple6 converts a [Tuple6] of [Either[E, T]] into an [Either[E, Tuple6]]. +func SequenceTuple6[E, T1, T2, T3, T4, T5, T6 any](t T.Tuple6[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6]]) Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return A.SequenceTuple6( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], E, T6], + t, + ) +} + +// TraverseTuple6 converts a [Tuple6] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple6]]. +func TraverseTuple6[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return A.TraverseTuple6( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], E, T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], E, T6], + f1, + f2, + f3, + f4, + f5, + f6, + t, + ) + } +} + +// Eitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning an Either +// The inverse function is [Uneitherize7] +func Eitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6)) + } +} + +// Uneitherize7 converts a function with 7 parameters returning an Either into a function with 7 parameters returning a tuple +// The inverse function is [Eitherize7] +func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) Either[error, R], T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6)) + } +} + +// SequenceT7 converts 7 parameters of [Either[E, T]] into a [Either[E, Tuple7]]. +func SequenceT7[E, T1, T2, T3, T4, T5, T6, T7 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7]) Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return A.SequenceT7( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceTuple7 converts a [Tuple7] of [Either[E, T]] into an [Either[E, Tuple7]]. +func SequenceTuple7[E, T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7]]) Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return A.SequenceTuple7( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T7], + t, + ) +} + +// TraverseTuple7 converts a [Tuple7] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple7]]. +func TraverseTuple7[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return A.TraverseTuple7( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], E, T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t, + ) + } +} + +// Eitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning an Either +// The inverse function is [Uneitherize8] +func Eitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7)) + } +} + +// Uneitherize8 converts a function with 8 parameters returning an Either into a function with 8 parameters returning a tuple +// The inverse function is [Eitherize8] +func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7)) + } +} + +// SequenceT8 converts 8 parameters of [Either[E, T]] into a [Either[E, Tuple8]]. +func SequenceT8[E, T1, T2, T3, T4, T5, T6, T7, T8 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8]) Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return A.SequenceT8( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceTuple8 converts a [Tuple8] of [Either[E, T]] into an [Either[E, Tuple8]]. +func SequenceTuple8[E, T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8]]) Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return A.SequenceTuple8( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T8], + t, + ) +} + +// TraverseTuple8 converts a [Tuple8] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple8]]. +func TraverseTuple8[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return A.TraverseTuple8( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], E, T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t, + ) + } +} + +// Eitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning an Either +// The inverse function is [Uneitherize9] +func Eitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8)) + } +} + +// Uneitherize9 converts a function with 9 parameters returning an Either into a function with 9 parameters returning a tuple +// The inverse function is [Eitherize9] +func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8)) + } +} + +// SequenceT9 converts 9 parameters of [Either[E, T]] into a [Either[E, Tuple9]]. +func SequenceT9[E, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9]) Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return A.SequenceT9( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceTuple9 converts a [Tuple9] of [Either[E, T]] into an [Either[E, Tuple9]]. +func SequenceTuple9[E, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9]]) Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return A.SequenceTuple9( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T9], + t, + ) +} + +// TraverseTuple9 converts a [Tuple9] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple9]]. +func TraverseTuple9[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return A.TraverseTuple9( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E, T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t, + ) + } +} + +// Eitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning an Either +// The inverse function is [Uneitherize10] +func Eitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)) + } +} + +// Uneitherize10 converts a function with 10 parameters returning an Either into a function with 10 parameters returning a tuple +// The inverse function is [Eitherize10] +func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)) + } +} + +// SequenceT10 converts 10 parameters of [Either[E, T]] into a [Either[E, Tuple10]]. +func SequenceT10[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9], t10 Either[E, T10]) Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return A.SequenceT10( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceTuple10 converts a [Tuple10] of [Either[E, T]] into an [Either[E, Tuple10]]. +func SequenceTuple10[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9], Either[E, T10]]) Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return A.SequenceTuple10( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T10], + t, + ) +} + +// TraverseTuple10 converts a [Tuple10] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple10]]. +func TraverseTuple10[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], F10 ~func(A10) Either[E, T10], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return A.TraverseTuple10( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E, T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t, + ) + } +} + +// Eitherize11 converts a function with 11 parameters returning a tuple into a function with 11 parameters returning an Either +// The inverse function is [Uneitherize11] +func Eitherize11[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)) + } +} + +// Uneitherize11 converts a function with 11 parameters returning an Either into a function with 11 parameters returning a tuple +// The inverse function is [Eitherize11] +func Uneitherize11[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)) + } +} + +// SequenceT11 converts 11 parameters of [Either[E, T]] into a [Either[E, Tuple11]]. +func SequenceT11[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9], t10 Either[E, T10], t11 Either[E, T11]) Either[E, T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] { + return A.SequenceT11( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T7], + Ap[func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T8], + Ap[func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T9], + Ap[func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T10], + Ap[T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T11], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + t11, + ) +} + +// SequenceTuple11 converts a [Tuple11] of [Either[E, T]] into an [Either[E, Tuple11]]. +func SequenceTuple11[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t T.Tuple11[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9], Either[E, T10], Either[E, T11]]) Either[E, T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] { + return A.SequenceTuple11( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T7], + Ap[func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T8], + Ap[func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T9], + Ap[func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T10], + Ap[T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T11], + t, + ) +} + +// TraverseTuple11 converts a [Tuple11] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple11]]. +func TraverseTuple11[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], F10 ~func(A10) Either[E, T10], F11 ~func(A11) Either[E, T11], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10, A11, T11 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) func(T.Tuple11[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11]) Either[E, T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] { + return func(t T.Tuple11[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11]) Either[E, T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] { + return A.TraverseTuple11( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T7], + Ap[func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T8], + Ap[func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T9], + Ap[func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T10], + Ap[T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E, T11], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + t, + ) + } +} + +// Eitherize12 converts a function with 12 parameters returning a tuple into a function with 12 parameters returning an Either +// The inverse function is [Uneitherize12] +func Eitherize12[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)) + } +} + +// Uneitherize12 converts a function with 12 parameters returning an Either into a function with 12 parameters returning a tuple +// The inverse function is [Eitherize12] +func Uneitherize12[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)) + } +} + +// SequenceT12 converts 12 parameters of [Either[E, T]] into a [Either[E, Tuple12]]. +func SequenceT12[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9], t10 Either[E, T10], t11 Either[E, T11], t12 Either[E, T12]) Either[E, T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] { + return A.SequenceT12( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T8], + Ap[func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T9], + Ap[func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T10], + Ap[func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T11], + Ap[T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T12], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + t11, + t12, + ) +} + +// SequenceTuple12 converts a [Tuple12] of [Either[E, T]] into an [Either[E, Tuple12]]. +func SequenceTuple12[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t T.Tuple12[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9], Either[E, T10], Either[E, T11], Either[E, T12]]) Either[E, T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] { + return A.SequenceTuple12( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T8], + Ap[func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T9], + Ap[func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T10], + Ap[func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T11], + Ap[T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T12], + t, + ) +} + +// TraverseTuple12 converts a [Tuple12] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple12]]. +func TraverseTuple12[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], F10 ~func(A10) Either[E, T10], F11 ~func(A11) Either[E, T11], F12 ~func(A12) Either[E, T12], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10, A11, T11, A12, T12 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) func(T.Tuple12[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12]) Either[E, T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] { + return func(t T.Tuple12[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12]) Either[E, T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] { + return A.TraverseTuple12( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T8], + Ap[func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T9], + Ap[func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T10], + Ap[func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T11], + Ap[T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E, T12], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + t, + ) + } +} + +// Eitherize13 converts a function with 13 parameters returning a tuple into a function with 13 parameters returning an Either +// The inverse function is [Uneitherize13] +func Eitherize13[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12)) + } +} + +// Uneitherize13 converts a function with 13 parameters returning an Either into a function with 13 parameters returning a tuple +// The inverse function is [Eitherize13] +func Uneitherize13[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12)) + } +} + +// SequenceT13 converts 13 parameters of [Either[E, T]] into a [Either[E, Tuple13]]. +func SequenceT13[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9], t10 Either[E, T10], t11 Either[E, T11], t12 Either[E, T12], t13 Either[E, T13]) Either[E, T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] { + return A.SequenceT13( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T9], + Ap[func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T10], + Ap[func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T11], + Ap[func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T12], + Ap[T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T13], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + t11, + t12, + t13, + ) +} + +// SequenceTuple13 converts a [Tuple13] of [Either[E, T]] into an [Either[E, Tuple13]]. +func SequenceTuple13[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t T.Tuple13[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9], Either[E, T10], Either[E, T11], Either[E, T12], Either[E, T13]]) Either[E, T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] { + return A.SequenceTuple13( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T9], + Ap[func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T10], + Ap[func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T11], + Ap[func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T12], + Ap[T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T13], + t, + ) +} + +// TraverseTuple13 converts a [Tuple13] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple13]]. +func TraverseTuple13[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], F10 ~func(A10) Either[E, T10], F11 ~func(A11) Either[E, T11], F12 ~func(A12) Either[E, T12], F13 ~func(A13) Either[E, T13], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10, A11, T11, A12, T12, A13, T13 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) func(T.Tuple13[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13]) Either[E, T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] { + return func(t T.Tuple13[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13]) Either[E, T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] { + return A.TraverseTuple13( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T9], + Ap[func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T10], + Ap[func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T11], + Ap[func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T12], + Ap[T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E, T13], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + t, + ) + } +} + +// Eitherize14 converts a function with 14 parameters returning a tuple into a function with 14 parameters returning an Either +// The inverse function is [Uneitherize14] +func Eitherize14[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13)) + } +} + +// Uneitherize14 converts a function with 14 parameters returning an Either into a function with 14 parameters returning a tuple +// The inverse function is [Eitherize14] +func Uneitherize14[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13)) + } +} + +// SequenceT14 converts 14 parameters of [Either[E, T]] into a [Either[E, Tuple14]]. +func SequenceT14[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9], t10 Either[E, T10], t11 Either[E, T11], t12 Either[E, T12], t13 Either[E, T13], t14 Either[E, T14]) Either[E, T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] { + return A.SequenceT14( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T9], + Ap[func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T10], + Ap[func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T11], + Ap[func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T12], + Ap[func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T13], + Ap[T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T14], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + t11, + t12, + t13, + t14, + ) +} + +// SequenceTuple14 converts a [Tuple14] of [Either[E, T]] into an [Either[E, Tuple14]]. +func SequenceTuple14[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t T.Tuple14[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9], Either[E, T10], Either[E, T11], Either[E, T12], Either[E, T13], Either[E, T14]]) Either[E, T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] { + return A.SequenceTuple14( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T9], + Ap[func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T10], + Ap[func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T11], + Ap[func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T12], + Ap[func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T13], + Ap[T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T14], + t, + ) +} + +// TraverseTuple14 converts a [Tuple14] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple14]]. +func TraverseTuple14[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], F10 ~func(A10) Either[E, T10], F11 ~func(A11) Either[E, T11], F12 ~func(A12) Either[E, T12], F13 ~func(A13) Either[E, T13], F14 ~func(A14) Either[E, T14], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10, A11, T11, A12, T12, A13, T13, A14, T14 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) func(T.Tuple14[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14]) Either[E, T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] { + return func(t T.Tuple14[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14]) Either[E, T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] { + return A.TraverseTuple14( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T9], + Ap[func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T10], + Ap[func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T11], + Ap[func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T12], + Ap[func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T13], + Ap[T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E, T14], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + t, + ) + } +} + +// Eitherize15 converts a function with 15 parameters returning a tuple into a function with 15 parameters returning an Either +// The inverse function is [Uneitherize15] +func Eitherize15[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) Either[error, R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) Either[error, R] { + return TryCatchError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14)) + } +} + +// Uneitherize15 converts a function with 15 parameters returning an Either into a function with 15 parameters returning a tuple +// The inverse function is [Eitherize15] +func Uneitherize15[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) (R, error) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) (R, error) { + return UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14)) + } +} + +// SequenceT15 converts 15 parameters of [Either[E, T]] into a [Either[E, Tuple15]]. +func SequenceT15[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t1 Either[E, T1], t2 Either[E, T2], t3 Either[E, T3], t4 Either[E, T4], t5 Either[E, T5], t6 Either[E, T6], t7 Either[E, T7], t8 Either[E, T8], t9 Either[E, T9], t10 Either[E, T10], t11 Either[E, T11], t12 Either[E, T12], t13 Either[E, T13], t14 Either[E, T14], t15 Either[E, T15]) Either[E, T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] { + return A.SequenceT15( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T9], + Ap[func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T10], + Ap[func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T11], + Ap[func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T12], + Ap[func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T13], + Ap[func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T14], + Ap[T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T15], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + t11, + t12, + t13, + t14, + t15, + ) +} + +// SequenceTuple15 converts a [Tuple15] of [Either[E, T]] into an [Either[E, Tuple15]]. +func SequenceTuple15[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t T.Tuple15[Either[E, T1], Either[E, T2], Either[E, T3], Either[E, T4], Either[E, T5], Either[E, T6], Either[E, T7], Either[E, T8], Either[E, T9], Either[E, T10], Either[E, T11], Either[E, T12], Either[E, T13], Either[E, T14], Either[E, T15]]) Either[E, T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] { + return A.SequenceTuple15( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T9], + Ap[func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T10], + Ap[func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T11], + Ap[func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T12], + Ap[func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T13], + Ap[func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T14], + Ap[T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T15], + t, + ) +} + +// TraverseTuple15 converts a [Tuple15] of [A] via transformation functions transforming [A] to [Either[E, A]] into a [Either[E, Tuple15]]. +func TraverseTuple15[F1 ~func(A1) Either[E, T1], F2 ~func(A2) Either[E, T2], F3 ~func(A3) Either[E, T3], F4 ~func(A4) Either[E, T4], F5 ~func(A5) Either[E, T5], F6 ~func(A6) Either[E, T6], F7 ~func(A7) Either[E, T7], F8 ~func(A8) Either[E, T8], F9 ~func(A9) Either[E, T9], F10 ~func(A10) Either[E, T10], F11 ~func(A11) Either[E, T11], F12 ~func(A12) Either[E, T12], F13 ~func(A13) Either[E, T13], F14 ~func(A14) Either[E, T14], F15 ~func(A15) Either[E, T15], E, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10, A11, T11, A12, T12, A13, T13, A14, T14, A15, T15 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) func(T.Tuple15[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15]) Either[E, T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] { + return func(t T.Tuple15[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15]) Either[E, T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] { + return A.TraverseTuple15( + Map[E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T5], + Ap[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T6], + Ap[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T7], + Ap[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T8], + Ap[func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T9], + Ap[func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T10], + Ap[func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T11], + Ap[func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T12], + Ap[func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T13], + Ap[func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T14], + Ap[T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E, T15], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + t, + ) + } +} diff --git a/v2/either/http/request.go b/v2/either/http/request.go new file mode 100644 index 0000000..d1326c8 --- /dev/null +++ b/v2/either/http/request.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "bytes" + "net/http" + + E "github.com/IBM/fp-go/v2/either" +) + +var ( + PostRequest = bodyRequest("POST") + PutRequest = bodyRequest("PUT") + + GetRequest = noBodyRequest("GET") + DeleteRequest = noBodyRequest("DELETE") + OptionsRequest = noBodyRequest("OPTIONS") + HeadRequest = noBodyRequest("HEAD") +) + +func bodyRequest(method string) func(string) func([]byte) E.Either[error, *http.Request] { + return func(url string) func([]byte) E.Either[error, *http.Request] { + return func(body []byte) E.Either[error, *http.Request] { + return E.TryCatchError(http.NewRequest(method, url, bytes.NewReader(body))) + } + } +} + +func noBodyRequest(method string) func(string) E.Either[error, *http.Request] { + return func(url string) E.Either[error, *http.Request] { + return E.TryCatchError(http.NewRequest(method, url, nil)) + } +} diff --git a/v2/either/logger.go b/v2/either/logger.go new file mode 100644 index 0000000..44d7541 --- /dev/null +++ b/v2/either/logger.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "log" + + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/logging" +) + +func _log[E, A any](left func(string, ...any), right func(string, ...any), prefix string) func(Either[E, A]) Either[E, A] { + return Fold( + func(e E) Either[E, A] { + left("%s: %v", prefix, e) + return Left[A, E](e) + }, + func(a A) Either[E, A] { + right("%s: %v", prefix, a) + return Right[E](a) + }) +} + +func Logger[E, A any](loggers ...*log.Logger) func(string) func(Either[E, A]) Either[E, A] { + left, right := L.LoggingCallbacks(loggers...) + return func(prefix string) func(Either[E, A]) Either[E, A] { + delegate := _log[E, A](left, right, prefix) + return func(ma Either[E, A]) Either[E, A] { + return F.Pipe1( + delegate(ma), + ChainTo[A](ma), + ) + } + } +} diff --git a/v2/either/logger_test.go b/v2/either/logger_test.go new file mode 100644 index 0000000..09c0208 --- /dev/null +++ b/v2/either/logger_test.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestLogger(t *testing.T) { + + l := Logger[error, string]() + + r := Right[error]("test") + + res := F.Pipe1( + r, + l("out"), + ) + + assert.Equal(t, r, res) +} diff --git a/v2/either/monad.go b/v2/either/monad.go new file mode 100644 index 0000000..035f09f --- /dev/null +++ b/v2/either/monad.go @@ -0,0 +1,43 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "github.com/IBM/fp-go/v2/internal/monad" +) + +type eitherMonad[E, A, B any] struct{} + +func (o *eitherMonad[E, A, B]) Of(a A) Either[E, A] { + return Of[E, A](a) +} + +func (o *eitherMonad[E, A, B]) Map(f func(A) B) func(Either[E, A]) Either[E, B] { + return Map[E, A, B](f) +} + +func (o *eitherMonad[E, A, B]) Chain(f func(A) Either[E, B]) func(Either[E, A]) Either[E, B] { + return Chain[E, A, B](f) +} + +func (o *eitherMonad[E, A, B]) Ap(fa Either[E, A]) func(Either[E, func(A) B]) Either[E, B] { + return Ap[B, E, A](fa) +} + +// Monad implements the monadic operations for [Either] +func Monad[E, A, B any]() monad.Monad[A, B, Either[E, A], Either[E, B], Either[E, func(A) B]] { + return &eitherMonad[E, A, B]{} +} diff --git a/v2/either/monoid.go b/v2/either/monoid.go new file mode 100644 index 0000000..0d73c70 --- /dev/null +++ b/v2/either/monoid.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + L "github.com/IBM/fp-go/v2/lazy" + M "github.com/IBM/fp-go/v2/monoid" +) + +// AlternativeMonoid is the alternative [Monoid] for an [Either] +func AlternativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] { + return M.AlternativeMonoid( + Of[E, A], + MonadMap[E, A, func(A) A], + MonadAp[A, E, A], + MonadAlt[E, A], + m, + ) +} + +// AltMonoid is the alternative [Monoid] for an [Either] +func AltMonoid[E, A any](zero L.Lazy[Either[E, A]]) M.Monoid[Either[E, A]] { + return M.AltMonoid( + zero, + MonadAlt[E, A], + ) +} diff --git a/v2/either/pointed.go b/v2/either/pointed.go new file mode 100644 index 0000000..b008d1f --- /dev/null +++ b/v2/either/pointed.go @@ -0,0 +1,31 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "github.com/IBM/fp-go/v2/internal/pointed" +) + +type eitherPointed[E, A any] struct{} + +func (o *eitherPointed[E, A]) Of(a A) Either[E, A] { + return Of[E, A](a) +} + +// Pointed implements the pointedic operations for [Either] +func Pointed[E, A any]() pointed.Pointed[A, Either[E, A]] { + return &eitherPointed[E, A]{} +} diff --git a/v2/either/record.go b/v2/either/record.go new file mode 100644 index 0000000..3196d1e --- /dev/null +++ b/v2/either/record.go @@ -0,0 +1,81 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + F "github.com/IBM/fp-go/v2/function" + RR "github.com/IBM/fp-go/v2/internal/record" +) + +// TraverseRecordG transforms a record of options into an option of a record +func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] { + return RR.Traverse[GA]( + Of[E, GB], + Map[E, GB, func(B) GB], + Ap[GB, E, B], + f, + ) +} + +// TraverseRecord transforms a record of eithers into an either of a record +func TraverseRecord[K comparable, E, A, B any](f func(A) Either[E, B]) func(map[K]A) Either[E, map[K]B] { + return TraverseRecordG[map[K]A, map[K]B](f) +} + +// TraverseRecordWithIndexG transforms a record of options into an option of a record +func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(K, A) Either[E, B]) func(GA) Either[E, GB] { + return RR.TraverseWithIndex[GA]( + Of[E, GB], + Map[E, GB, func(B) GB], + Ap[GB, E, B], + f, + ) +} + +// TraverseRecordWithIndex transforms a record of eithers into an either of a record +func TraverseRecordWithIndex[K comparable, E, A, B any](f func(K, A) Either[E, B]) func(map[K]A) Either[E, map[K]B] { + return TraverseRecordWithIndexG[map[K]A, map[K]B](f) +} + +func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A any](ma GOA) Either[E, GA] { + return TraverseRecordG[GOA, GA](F.Identity[Either[E, A]])(ma) +} + +// SequenceRecord converts a homogeneous sequence of either into an either of sequence +func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] { + return SequenceRecordG[map[K]A](ma) +} + +func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M { + r[k] = v + return r +} + +// CompactRecordG discards the noe values and keeps the some values +func CompactRecordG[M1 ~map[K]Either[E, A], M2 ~map[K]A, K comparable, E, A any](m M1) M2 { + out := make(M2) + onLeft := F.Constant1[E](out) + return RR.ReduceWithIndex(m, func(key K, _ M2, value Either[E, A]) M2 { + return MonadFold(value, onLeft, func(v A) M2 { + return upsertAtReadWrite(out, key, v) + }) + }, out) +} + +// CompactRecord discards all none values and keeps the somes +func CompactRecord[K comparable, E, A any](m map[K]Either[E, A]) map[K]A { + return CompactRecordG[map[K]Either[E, A], map[K]A](m) +} diff --git a/v2/either/record_test.go b/v2/either/record_test.go new file mode 100644 index 0000000..8307b5d --- /dev/null +++ b/v2/either/record_test.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCompactRecord(t *testing.T) { + // make the map + m := make(map[string]Either[string, int]) + m["foo"] = Left[int]("error") + m["bar"] = Right[string](1) + // compact it + m1 := CompactRecord(m) + // check expected + exp := map[string]int{ + "bar": 1, + } + + assert.Equal(t, exp, m1) +} diff --git a/v2/either/resource.go b/v2/either/resource.go new file mode 100644 index 0000000..0cee327 --- /dev/null +++ b/v2/either/resource.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[E, R, A any](onCreate func() Either[E, R], onRelease func(R) Either[E, any]) func(func(R) Either[E, A]) Either[E, A] { + + return func(f func(R) Either[E, A]) Either[E, A] { + return MonadChain( + onCreate(), func(r R) Either[E, A] { + // run the code and make sure to release as quickly as possible + res := f(r) + released := onRelease(r) + // handle the errors + return MonadFold( + res, + Left[A, E], + func(a A) Either[E, A] { + return F.Pipe1( + released, + MapTo[E, any](a), + ) + }) + }, + ) + } +} diff --git a/v2/either/resource_test.go b/v2/either/resource_test.go new file mode 100644 index 0000000..42c279e --- /dev/null +++ b/v2/either/resource_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "os" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestWithResource(t *testing.T) { + onCreate := func() Either[error, *os.File] { + return TryCatchError(os.CreateTemp("", "*")) + } + onDelete := F.Flow2( + func(f *os.File) Either[error, string] { + return TryCatchError(f.Name(), f.Close()) + }, + Chain(func(name string) Either[error, any] { + return TryCatchError(any(name), os.Remove(name)) + }), + ) + + onHandler := func(f *os.File) Either[error, string] { + return Of[error](f.Name()) + } + + tempFile := WithResource[error, *os.File, string](onCreate, onDelete) + + resE := tempFile(onHandler) + + assert.True(t, IsRight(resE)) +} diff --git a/v2/either/semigroup.go b/v2/either/semigroup.go new file mode 100644 index 0000000..e8ae9e2 --- /dev/null +++ b/v2/either/semigroup.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + S "github.com/IBM/fp-go/v2/semigroup" +) + +// AltSemigroup is the alternative [Semigroup] for an [Either] +func AltSemigroup[E, A any]() S.Semigroup[Either[E, A]] { + return S.AltSemigroup( + MonadAlt[E, A], + ) +} diff --git a/v2/either/testing/laws.go b/v2/either/testing/laws.go new file mode 100644 index 0000000..8399a50 --- /dev/null +++ b/v2/either/testing/laws.go @@ -0,0 +1,75 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" +) + +// AssertLaws asserts the apply monad laws for the `Either` monad +func AssertLaws[E, A, B, C any](t *testing.T, + eqe EQ.Eq[E], + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + return L.AssertLaws(t, + ET.Eq(eqe, eqa), + ET.Eq(eqe, eqb), + ET.Eq(eqe, eqc), + + ET.Of[E, A], + ET.Of[E, B], + ET.Of[E, C], + + ET.Of[E, func(A) A], + ET.Of[E, func(A) B], + ET.Of[E, func(B) C], + ET.Of[E, func(func(A) B) B], + + ET.MonadMap[E, A, A], + ET.MonadMap[E, A, B], + ET.MonadMap[E, A, C], + ET.MonadMap[E, B, C], + + ET.MonadMap[E, func(B) C, func(func(A) B) func(A) C], + + ET.MonadChain[E, A, A], + ET.MonadChain[E, A, B], + ET.MonadChain[E, A, C], + ET.MonadChain[E, B, C], + + ET.MonadAp[A, E, A], + ET.MonadAp[B, E, A], + ET.MonadAp[C, E, B], + ET.MonadAp[C, E, A], + + ET.MonadAp[B, E, func(A) B], + ET.MonadAp[func(A) C, E, func(A) B], + + ab, + bc, + ) + +} diff --git a/v2/either/testing/laws_test.go b/v2/either/testing/laws_test.go new file mode 100644 index 0000000..8d2ec6a --- /dev/null +++ b/v2/either/testing/laws_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqe := EQ.FromStrictEquals[string]() + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqe, eqa, eqb, eqc, ab, bc) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/either/traverse.go b/v2/either/traverse.go new file mode 100644 index 0000000..8e7278a --- /dev/null +++ b/v2/either/traverse.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// Traverse converts an [Either] of some higher kinded type into the higher kinded type of an [Either] +func Traverse[A, E, B, HKTB, HKTRB any]( + mof func(Either[E, B]) HKTRB, + mmap func(func(B) Either[E, B]) func(HKTB) HKTRB, +) func(func(A) HKTB) func(Either[E, A]) HKTRB { + + left := F.Flow2(Left[B, E], mof) + right := mmap(Right[E, B]) + + return func(f func(A) HKTB) func(Either[E, A]) HKTRB { + return Fold(left, F.Flow2(f, right)) + } +} + +// Sequence converts an [Either] of some higher kinded type into the higher kinded type of an [Either] +func Sequence[E, A, HKTA, HKTRA any]( + mof func(Either[E, A]) HKTRA, + mmap func(func(A) Either[E, A]) func(HKTA) HKTRA, +) func(Either[E, HKTA]) HKTRA { + return Fold(F.Flow2(Left[A, E], mof), mmap(Right[E, A])) +} diff --git a/v2/either/traverse_test.go b/v2/either/traverse_test.go new file mode 100644 index 0000000..cb51695 --- /dev/null +++ b/v2/either/traverse_test.go @@ -0,0 +1,53 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +func TestTraverse(t *testing.T) { + f := func(n int) O.Option[int] { + if n >= 2 { + return O.Of(n) + } + return O.None[int]() + } + trav := Traverse[int]( + O.Of[Either[string, int]], + O.Map[int, Either[string, int]], + )(f) + + assert.Equal(t, O.Of(Left[int]("a")), F.Pipe1(Left[int]("a"), trav)) + assert.Equal(t, O.None[Either[string, int]](), F.Pipe1(Right[string](1), trav)) + assert.Equal(t, O.Of(Right[string](3)), F.Pipe1(Right[string](3), trav)) +} + +func TestSequence(t *testing.T) { + + seq := Sequence( + O.Of[Either[string, int]], + O.Map[int, Either[string, int]], + ) + + assert.Equal(t, O.Of(Right[string](1)), seq(Right[string](O.Of(1)))) + assert.Equal(t, O.Of(Left[int]("a")), seq(Left[O.Option[int]]("a"))) + assert.Equal(t, O.None[Either[string, int]](), seq(Right[string](O.None[int]()))) +} diff --git a/v2/either/variadic.go b/v2/either/variadic.go new file mode 100644 index 0000000..0500f8c --- /dev/null +++ b/v2/either/variadic.go @@ -0,0 +1,76 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +func Variadic0[V, R any](f func([]V) (R, error)) func(...V) Either[error, R] { + return func(v ...V) Either[error, R] { + return TryCatchError(f(v)) + } +} + +func Variadic1[T1, V, R any](f func(T1, []V) (R, error)) func(T1, ...V) Either[error, R] { + return func(t1 T1, v ...V) Either[error, R] { + return TryCatchError(f(t1, v)) + } +} + +func Variadic2[T1, T2, V, R any](f func(T1, T2, []V) (R, error)) func(T1, T2, ...V) Either[error, R] { + return func(t1 T1, t2 T2, v ...V) Either[error, R] { + return TryCatchError(f(t1, t2, v)) + } +} + +func Variadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, []V) (R, error)) func(T1, T2, T3, ...V) Either[error, R] { + return func(t1 T1, t2 T2, t3 T3, v ...V) Either[error, R] { + return TryCatchError(f(t1, t2, t3, v)) + } +} + +func Variadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, []V) (R, error)) func(T1, T2, T3, T4, ...V) Either[error, R] { + return func(t1 T1, t2 T2, t3 T3, t4 T4, v ...V) Either[error, R] { + return TryCatchError(f(t1, t2, t3, t4, v)) + } +} + +func Unvariadic0[V, R any](f func(...V) (R, error)) func([]V) Either[error, R] { + return func(v []V) Either[error, R] { + return TryCatchError(f(v...)) + } +} + +func Unvariadic1[T1, V, R any](f func(T1, ...V) (R, error)) func(T1, []V) Either[error, R] { + return func(t1 T1, v []V) Either[error, R] { + return TryCatchError(f(t1, v...)) + } +} + +func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) (R, error)) func(T1, T2, []V) Either[error, R] { + return func(t1 T1, t2 T2, v []V) Either[error, R] { + return TryCatchError(f(t1, t2, v...)) + } +} + +func Unvariadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, ...V) (R, error)) func(T1, T2, T3, []V) Either[error, R] { + return func(t1 T1, t2 T2, t3 T3, v []V) Either[error, R] { + return TryCatchError(f(t1, t2, t3, v...)) + } +} + +func Unvariadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, ...V) (R, error)) func(T1, T2, T3, T4, []V) Either[error, R] { + return func(t1 T1, t2 T2, t3 T3, t4 T4, v []V) Either[error, R] { + return TryCatchError(f(t1, t2, t3, t4, v...)) + } +} diff --git a/v2/endomorphism/curry.go b/v2/endomorphism/curry.go new file mode 100644 index 0000000..1a8b258 --- /dev/null +++ b/v2/endomorphism/curry.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package endomorphism + +import ( + G "github.com/IBM/fp-go/v2/endomorphism/generic" +) + +// Curry2 curries a binary function +func Curry2[FCT ~func(T0, T1) T1, T0, T1 any](f FCT) func(T0) Endomorphism[T1] { + return G.Curry2[Endomorphism[T1]](f) +} + +// Curry3 curries a ternary function +func Curry3[FCT ~func(T0, T1, T2) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) Endomorphism[T2] { + return G.Curry3[Endomorphism[T2]](f) +} diff --git a/v2/endomorphism/endo.go b/v2/endomorphism/endo.go new file mode 100644 index 0000000..2a0d8dc --- /dev/null +++ b/v2/endomorphism/endo.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package endomorphism + +import ( + G "github.com/IBM/fp-go/v2/endomorphism/generic" +) + +func MonadAp[A any](fab Endomorphism[A], fa A) A { + return G.MonadAp[Endomorphism[A]](fab, fa) +} + +func Ap[A any](fa A) func(Endomorphism[A]) A { + return G.Ap[Endomorphism[A]](fa) +} + +func MonadChain[A any](ma Endomorphism[A], f Endomorphism[A]) Endomorphism[A] { + return G.MonadChain[Endomorphism[A]](ma, f) +} + +func Chain[A any](f Endomorphism[A]) Endomorphism[Endomorphism[A]] { + return G.Chain[Endomorphism[Endomorphism[A]], Endomorphism[A], A](f) +} diff --git a/v2/endomorphism/generic/curry.go b/v2/endomorphism/generic/curry.go new file mode 100644 index 0000000..e7e41e2 --- /dev/null +++ b/v2/endomorphism/generic/curry.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +// Curry2 is a duplicate of [F.Curry2] but because of the type system it's not compatible otherwise +func Curry2[GT1 ~func(T1) T1, FCT ~func(T0, T1) T1, T0, T1 any](f FCT) func(T0) GT1 { + return func(t0 T0) GT1 { + return func(t1 T1) T1 { + return f(t0, t1) + } + } +} + +// Curry2 is a duplicate of [F.Curry2] but because of the type system it's not compatible otherwise +func Curry3[GT2 ~func(T2) T2, FCT ~func(T0, T1, T2) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) GT2 { + return func(t0 T0) func(T1) GT2 { + return func(t1 T1) GT2 { + return func(t2 T2) T2 { + return f(t0, t1, t2) + } + } + } +} diff --git a/v2/endomorphism/generic/endo.go b/v2/endomorphism/generic/endo.go new file mode 100644 index 0000000..32c3b6c --- /dev/null +++ b/v2/endomorphism/generic/endo.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity/generic" +) + +func MonadAp[GA ~func(A) A, A any](fab GA, fa A) A { + return I.MonadAp[GA, A, A](fab, fa) +} + +func Ap[GA ~func(A) A, A any](fa A) func(GA) A { + return I.Ap[GA, A, A](fa) +} + +func MonadChain[GA ~func(A) A, A any](ma GA, f GA) GA { + return Compose(ma, f) +} + +func Chain[ENDO ~func(GA) GA, GA ~func(A) A, A any](f GA) ENDO { + return Of[ENDO](F.Bind2nd(Compose[GA], f)) +} diff --git a/v2/endomorphism/generic/monoid.go b/v2/endomorphism/generic/monoid.go new file mode 100644 index 0000000..d83256e --- /dev/null +++ b/v2/endomorphism/generic/monoid.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// Of converts any function to an [Endomorphism] +func Of[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO { + return func(a A) A { + return f(a) + } +} + +// Wrap converts any function to an [Endomorphism] +func Wrap[ENDO ~func(A) A, F ~func(A) A, A any](f F) ENDO { + return Of[ENDO](f) +} + +// Unwrap converts any [Endomorphism] to a normal function +func Unwrap[F ~func(A) A, ENDO ~func(A) A, A any](f ENDO) F { + return Of[F](f) +} + +func Identity[ENDO ~func(A) A, A any]() ENDO { + return Of[ENDO](F.Identity[A]) +} + +func Compose[ENDO ~func(A) A, A any](f1, f2 ENDO) ENDO { + return Of[ENDO](F.Flow2(f1, f2)) +} + +// Semigroup for the Endomorphism where the `concat` operation is the usual function composition. +func Semigroup[ENDO ~func(A) A, A any]() S.Semigroup[ENDO] { + return S.MakeSemigroup(Compose[ENDO]) +} + +// Monoid for the Endomorphism where the `concat` operation is the usual function composition. +func Monoid[ENDO ~func(A) A, A any]() M.Monoid[ENDO] { + return M.MakeMonoid(Compose[ENDO], Identity[ENDO]()) +} diff --git a/v2/endomorphism/monoid.go b/v2/endomorphism/monoid.go new file mode 100644 index 0000000..b86ede0 --- /dev/null +++ b/v2/endomorphism/monoid.go @@ -0,0 +1,55 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package endomorphism + +import ( + G "github.com/IBM/fp-go/v2/endomorphism/generic" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// Endomorphism is a function that +type Endomorphism[A any] func(A) A + +// Of converts any function to an [Endomorphism] +func Of[F ~func(A) A, A any](f F) Endomorphism[A] { + return G.Of[Endomorphism[A]](f) +} + +// Wrap converts any function to an [Endomorphism] +func Wrap[F ~func(A) A, A any](f F) Endomorphism[A] { + return G.Wrap[Endomorphism[A]](f) +} + +// Unwrap converts any [Endomorphism] to a function +func Unwrap[F ~func(A) A, A any](f Endomorphism[A]) F { + return G.Unwrap[F](f) +} + +// Identity returns the identity [Endomorphism] +func Identity[A any]() Endomorphism[A] { + return G.Identity[Endomorphism[A]]() +} + +// Semigroup for the Endomorphism where the `concat` operation is the usual function composition. +func Semigroup[A any]() S.Semigroup[Endomorphism[A]] { + return G.Semigroup[Endomorphism[A]]() +} + +// Monoid for the Endomorphism where the `concat` operation is the usual function composition. +func Monoid[A any]() M.Monoid[Endomorphism[A]] { + return G.Monoid[Endomorphism[A]]() +} diff --git a/v2/eq/contramap.go b/v2/eq/contramap.go new file mode 100644 index 0000000..8056e06 --- /dev/null +++ b/v2/eq/contramap.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eq + +// Contramap implements an Equals predicate based on a mapping +func Contramap[A, B any](f func(b B) A) func(Eq[A]) Eq[B] { + return func(fa Eq[A]) Eq[B] { + equals := fa.Equals + return FromEquals(func(x, y B) bool { + return equals(f(x), f(y)) + }) + } +} diff --git a/v2/eq/eq.go b/v2/eq/eq.go new file mode 100644 index 0000000..7a398ca --- /dev/null +++ b/v2/eq/eq.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eq + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +type Eq[T any] interface { + Equals(x, y T) bool +} + +type eq[T any] struct { + c func(x, y T) bool +} + +func (e eq[T]) Equals(x, y T) bool { + return e.c(x, y) +} + +func strictEq[A comparable](a, b A) bool { + return a == b +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[T comparable]() Eq[T] { + return FromEquals(strictEq[T]) +} + +// FromEquals constructs an [EQ.Eq] from the comparison function +func FromEquals[T any](c func(x, y T) bool) Eq[T] { + return eq[T]{c: c} +} + +// Empty returns the equals predicate that is always true +func Empty[T any]() Eq[T] { + return FromEquals(F.Constant2[T, T](true)) +} + +// Equals returns a predicate to test if one value equals the other under an equals predicate +func Equals[T any](eq Eq[T]) func(T) func(T) bool { + return func(other T) func(T) bool { + return F.Bind2nd(eq.Equals, other) + } +} diff --git a/v2/eq/monoid.go b/v2/eq/monoid.go new file mode 100644 index 0000000..a86c5b3 --- /dev/null +++ b/v2/eq/monoid.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eq + +import ( + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func Semigroup[A any]() S.Semigroup[Eq[A]] { + return S.MakeSemigroup(func(x, y Eq[A]) Eq[A] { + return FromEquals(func(a, b A) bool { + return x.Equals(a, b) && y.Equals(a, b) + }) + }) +} + +func Monoid[A any]() M.Monoid[Eq[A]] { + return M.MakeMonoid(Semigroup[A]().Concat, Empty[A]()) +} diff --git a/v2/eq/testing/eq.go b/v2/eq/testing/eq.go new file mode 100644 index 0000000..a5c2c9c --- /dev/null +++ b/v2/eq/testing/eq.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +// Eq implements the equal operation based on `ObjectsAreEqualValues` from the assertion library +func Eq[A any]() EQ.Eq[A] { + return EQ.FromEquals(func(l, r A) bool { + return assert.ObjectsAreEqualValues(l, r) + }) +} diff --git a/v2/erasure/erasure.go b/v2/erasure/erasure.go new file mode 100644 index 0000000..42faa06 --- /dev/null +++ b/v2/erasure/erasure.go @@ -0,0 +1,62 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package erasure + +import ( + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" +) + +// Erase converts a variable of type T to an any by returning a pointer to that variable +func Erase[T any](t T) any { + return &t +} + +// Unerase converts an erased variable back to its original value +func Unerase[T any](t any) T { + return *t.(*T) +} + +// SafeUnerase converts an erased variable back to its original value +func SafeUnerase[T any](t any) E.Either[error, T] { + return F.Pipe2( + t, + E.ToType[*T](errors.OnSome[any]("Value of type [%T] is not erased")), + E.Map[error](F.Deref[T]), + ) +} + +// Erase0 converts a type safe function into an erased function +func Erase0[T1 any](f func() T1) func() any { + return F.Nullary2(f, Erase[T1]) +} + +// Erase1 converts a type safe function into an erased function +func Erase1[T1, T2 any](f func(T1) T2) func(any) any { + return F.Flow3( + Unerase[T1], + f, + Erase[T2], + ) +} + +// Erase2 converts a type safe function into an erased function +func Erase2[T1, T2, T3 any](f func(T1, T2) T3) func(any, any) any { + return func(t1, t2 any) any { + return Erase(f(Unerase[T1](t1), Unerase[T2](t2))) + } +} diff --git a/v2/erasure/erasure_test.go b/v2/erasure/erasure_test.go new file mode 100644 index 0000000..e83fcc5 --- /dev/null +++ b/v2/erasure/erasure_test.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package erasure + +import ( + "fmt" + "strings" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" +) + +func TestEither(t *testing.T) { + + e1 := F.Pipe3( + E.Of[error](Erase("Carsten")), + E.Map[error](Erase1(strings.ToUpper)), + E.GetOrElse(func(e error) any { + return Erase("Error") + }), + Unerase[string], + ) + + fmt.Println(e1) +} diff --git a/v2/errors/conv.go b/v2/errors/conv.go new file mode 100644 index 0000000..37878b3 --- /dev/null +++ b/v2/errors/conv.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "errors" + + O "github.com/IBM/fp-go/v2/option" +) + +// As tries to extract the error of desired type from the given error +func As[A error]() func(error) O.Option[A] { + return O.FromValidation(func(err error) (A, bool) { + var a A + ok := errors.As(err, &a) + return a, ok + }) +} diff --git a/v2/errors/conv_test.go b/v2/errors/conv_test.go new file mode 100644 index 0000000..df4e573 --- /dev/null +++ b/v2/errors/conv_test.go @@ -0,0 +1,54 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "fmt" + "testing" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +type MyError struct{} + +func (m *MyError) Error() string { + return "boom" +} + +func TestAs(t *testing.T) { + root := &MyError{} + err := fmt.Errorf("This is my custom error, %w", root) + + errO := F.Pipe1( + err, + As[*MyError](), + ) + + assert.Equal(t, O.Of(root), errO) +} + +func TestNotAs(t *testing.T) { + err := fmt.Errorf("This is my custom error") + + errO := F.Pipe1( + err, + As[*MyError](), + ) + + assert.Equal(t, O.None[*MyError](), errO) +} diff --git a/v2/errors/identity.go b/v2/errors/identity.go new file mode 100644 index 0000000..85bdd20 --- /dev/null +++ b/v2/errors/identity.go @@ -0,0 +1,22 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +var IdentityError = F.Identity[error] diff --git a/v2/errors/message_test.go b/v2/errors/message_test.go new file mode 100644 index 0000000..a68e125 --- /dev/null +++ b/v2/errors/message_test.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOnError(t *testing.T) { + onError := OnError("Failed to process [%s]", "filename") + + err := fmt.Errorf("Cause") + + err1 := onError(err) + + assert.Equal(t, "Failed to process [filename], Caused By: Cause", err1.Error()) +} diff --git a/v2/errors/messages.go b/v2/errors/messages.go new file mode 100644 index 0000000..79c8db1 --- /dev/null +++ b/v2/errors/messages.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errors + +import ( + "fmt" + + A "github.com/IBM/fp-go/v2/array" +) + +// OnNone generates a nullary function that produces a formatted error +func OnNone(msg string, args ...any) func() error { + return func() error { + return fmt.Errorf(msg, args...) + } +} + +// OnSome generates a unary function that produces a formatted error +func OnSome[T any](msg string, args ...any) func(T) error { + l := len(args) + if l == 0 { + return func(value T) error { + return fmt.Errorf(msg, value) + } + } + return func(value T) error { + data := make([]any, l) + copy(data[1:], args) + data[0] = value + return fmt.Errorf(msg, data...) + } +} + +// OnError generates a unary function that produces a formatted error. The argument +// to that function is the root cause of the error and the message will be augmented with +// a format string containing %w +func OnError(msg string, args ...any) func(error) error { + return func(err error) error { + return fmt.Errorf(msg+", Caused By: %w", A.ArrayConcatAll(args, A.Of[any](err))...) + } +} + +// ToString converts an error to a string +func ToString(err error) string { + return err.Error() +} diff --git a/v2/exec/exec.go b/v2/exec/exec.go new file mode 100644 index 0000000..2b83746 --- /dev/null +++ b/v2/exec/exec.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package exec + +import ( + P "github.com/IBM/fp-go/v2/pair" +) + +type ( + // CommandOutput represents the output of executing a command. The first field in the [Tuple2] is + // stdout, the second one is stderr. Use [StdOut] and [StdErr] to access these fields + CommandOutput = P.Pair[[]byte, []byte] +) + +var ( + // StdOut returns the field of a [CommandOutput] representing `stdout` + StdOut = P.Head[[]byte, []byte] + // StdErr returns the field of a [CommandOutput] representing `stderr` + StdErr = P.Tail[[]byte, []byte] +) diff --git a/v2/file/getters.go b/v2/file/getters.go new file mode 100644 index 0000000..73ade59 --- /dev/null +++ b/v2/file/getters.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "io" + "path/filepath" +) + +// Join appends a filename to a root path +func Join(name string) func(root string) string { + return func(root string) string { + return filepath.Join(root, name) + } +} + +// ToReader converts a [io.Reader] +func ToReader[R io.Reader](r R) io.Reader { + return r +} + +// ToWriter converts a [io.Writer] +func ToWriter[W io.Writer](w W) io.Writer { + return w +} + +// ToCloser converts a [io.Closer] +func ToCloser[C io.Closer](c C) io.Closer { + return c +} diff --git a/v2/function/bind.go b/v2/function/bind.go new file mode 100644 index 0000000..ff84390 --- /dev/null +++ b/v2/function/bind.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +func Bind1st[T1, T2, R any](f func(T1, T2) R, t1 T1) func(T2) R { + return func(t2 T2) R { + return f(t1, t2) + } +} +func Bind2nd[T1, T2, R any](f func(T1, T2) R, t2 T2) func(T1) R { + return func(t1 T1) R { + return f(t1, t2) + } +} + +// SK function (SKI combinator calculus). +func SK[T1, T2 any](_ T1, t2 T2) T2 { + return t2 +} diff --git a/v2/function/binds.go b/v2/function/binds.go new file mode 100644 index 0000000..6528b09 --- /dev/null +++ b/v2/function/binds.go @@ -0,0 +1,455 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:01.120389 +0100 CET m=+0.011216001 + +package function + +// Combinations for a total of 1 arguments + +// Bind1of1 takes a function with 1 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function. +// The return value of is a function with the remaining 0 parameters at positions [] of the original function. +func Bind1of1[F ~func(T1) R, T1, R any](f F) func(T1) func() R { + return func(t1 T1) func() R { + return func() R { + return f(t1) + } + } +} + +// Ignore1of1 takes a function with 0 parameters and returns a new function with 1 parameters that will ignore the values at positions [1] and pass the remaining 0 parameters to the original function +func Ignore1of1[T1 any, F ~func() R, R any](f F) func(T1) R { + return func(t1 T1) R { + return f() + } +} + +// Combinations for a total of 2 arguments + +// Bind1of2 takes a function with 2 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [2] of the original function. +func Bind1of2[F ~func(T1, T2) R, T1, T2, R any](f F) func(T1) func(T2) R { + return func(t1 T1) func(T2) R { + return func(t2 T2) R { + return f(t1, t2) + } + } +} + +// Ignore1of2 takes a function with 1 parameters and returns a new function with 2 parameters that will ignore the values at positions [1] and pass the remaining 1 parameters to the original function +func Ignore1of2[T1 any, F ~func(T2) R, T2, R any](f F) func(T1, T2) R { + return func(t1 T1, t2 T2) R { + return f(t2) + } +} + +// Bind2of2 takes a function with 2 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [2] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [1] of the original function. +func Bind2of2[F ~func(T1, T2) R, T1, T2, R any](f F) func(T2) func(T1) R { + return func(t2 T2) func(T1) R { + return func(t1 T1) R { + return f(t1, t2) + } + } +} + +// Ignore2of2 takes a function with 1 parameters and returns a new function with 2 parameters that will ignore the values at positions [2] and pass the remaining 1 parameters to the original function +func Ignore2of2[T2 any, F ~func(T1) R, T1, R any](f F) func(T1, T2) R { + return func(t1 T1, t2 T2) R { + return f(t1) + } +} + +// Bind12of2 takes a function with 2 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 2] of the original function. +// The return value of is a function with the remaining 0 parameters at positions [] of the original function. +func Bind12of2[F ~func(T1, T2) R, T1, T2, R any](f F) func(T1, T2) func() R { + return func(t1 T1, t2 T2) func() R { + return func() R { + return f(t1, t2) + } + } +} + +// Ignore12of2 takes a function with 0 parameters and returns a new function with 2 parameters that will ignore the values at positions [1, 2] and pass the remaining 0 parameters to the original function +func Ignore12of2[T1, T2 any, F ~func() R, R any](f F) func(T1, T2) R { + return func(t1 T1, t2 T2) R { + return f() + } +} + +// Combinations for a total of 3 arguments + +// Bind1of3 takes a function with 3 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [2, 3] of the original function. +func Bind1of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1) func(T2, T3) R { + return func(t1 T1) func(T2, T3) R { + return func(t2 T2, t3 T3) R { + return f(t1, t2, t3) + } + } +} + +// Ignore1of3 takes a function with 2 parameters and returns a new function with 3 parameters that will ignore the values at positions [1] and pass the remaining 2 parameters to the original function +func Ignore1of3[T1 any, F ~func(T2, T3) R, T2, T3, R any](f F) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f(t2, t3) + } +} + +// Bind2of3 takes a function with 3 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [2] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [1, 3] of the original function. +func Bind2of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T2) func(T1, T3) R { + return func(t2 T2) func(T1, T3) R { + return func(t1 T1, t3 T3) R { + return f(t1, t2, t3) + } + } +} + +// Ignore2of3 takes a function with 2 parameters and returns a new function with 3 parameters that will ignore the values at positions [2] and pass the remaining 2 parameters to the original function +func Ignore2of3[T2 any, F ~func(T1, T3) R, T1, T3, R any](f F) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f(t1, t3) + } +} + +// Bind3of3 takes a function with 3 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [3] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [1, 2] of the original function. +func Bind3of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T3) func(T1, T2) R { + return func(t3 T3) func(T1, T2) R { + return func(t1 T1, t2 T2) R { + return f(t1, t2, t3) + } + } +} + +// Ignore3of3 takes a function with 2 parameters and returns a new function with 3 parameters that will ignore the values at positions [3] and pass the remaining 2 parameters to the original function +func Ignore3of3[T3 any, F ~func(T1, T2) R, T1, T2, R any](f F) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f(t1, t2) + } +} + +// Bind12of3 takes a function with 3 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 2] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [3] of the original function. +func Bind12of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1, T2) func(T3) R { + return func(t1 T1, t2 T2) func(T3) R { + return func(t3 T3) R { + return f(t1, t2, t3) + } + } +} + +// Ignore12of3 takes a function with 1 parameters and returns a new function with 3 parameters that will ignore the values at positions [1, 2] and pass the remaining 1 parameters to the original function +func Ignore12of3[T1, T2 any, F ~func(T3) R, T3, R any](f F) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f(t3) + } +} + +// Bind13of3 takes a function with 3 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 3] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [2] of the original function. +func Bind13of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1, T3) func(T2) R { + return func(t1 T1, t3 T3) func(T2) R { + return func(t2 T2) R { + return f(t1, t2, t3) + } + } +} + +// Ignore13of3 takes a function with 1 parameters and returns a new function with 3 parameters that will ignore the values at positions [1, 3] and pass the remaining 1 parameters to the original function +func Ignore13of3[T1, T3 any, F ~func(T2) R, T2, R any](f F) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f(t2) + } +} + +// Bind23of3 takes a function with 3 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [2, 3] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [1] of the original function. +func Bind23of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T2, T3) func(T1) R { + return func(t2 T2, t3 T3) func(T1) R { + return func(t1 T1) R { + return f(t1, t2, t3) + } + } +} + +// Ignore23of3 takes a function with 1 parameters and returns a new function with 3 parameters that will ignore the values at positions [2, 3] and pass the remaining 1 parameters to the original function +func Ignore23of3[T2, T3 any, F ~func(T1) R, T1, R any](f F) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f(t1) + } +} + +// Bind123of3 takes a function with 3 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [1, 2, 3] of the original function. +// The return value of is a function with the remaining 0 parameters at positions [] of the original function. +func Bind123of3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1, T2, T3) func() R { + return func(t1 T1, t2 T2, t3 T3) func() R { + return func() R { + return f(t1, t2, t3) + } + } +} + +// Ignore123of3 takes a function with 0 parameters and returns a new function with 3 parameters that will ignore the values at positions [1, 2, 3] and pass the remaining 0 parameters to the original function +func Ignore123of3[T1, T2, T3 any, F ~func() R, R any](f F) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f() + } +} + +// Combinations for a total of 4 arguments + +// Bind1of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [1] of the original function. +// The return value of is a function with the remaining 3 parameters at positions [2, 3, 4] of the original function. +func Bind1of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1) func(T2, T3, T4) R { + return func(t1 T1) func(T2, T3, T4) R { + return func(t2 T2, t3 T3, t4 T4) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore1of4 takes a function with 3 parameters and returns a new function with 4 parameters that will ignore the values at positions [1] and pass the remaining 3 parameters to the original function +func Ignore1of4[T1 any, F ~func(T2, T3, T4) R, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t2, t3, t4) + } +} + +// Bind2of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [2] of the original function. +// The return value of is a function with the remaining 3 parameters at positions [1, 3, 4] of the original function. +func Bind2of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T2) func(T1, T3, T4) R { + return func(t2 T2) func(T1, T3, T4) R { + return func(t1 T1, t3 T3, t4 T4) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore2of4 takes a function with 3 parameters and returns a new function with 4 parameters that will ignore the values at positions [2] and pass the remaining 3 parameters to the original function +func Ignore2of4[T2 any, F ~func(T1, T3, T4) R, T1, T3, T4, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t1, t3, t4) + } +} + +// Bind3of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [3] of the original function. +// The return value of is a function with the remaining 3 parameters at positions [1, 2, 4] of the original function. +func Bind3of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T3) func(T1, T2, T4) R { + return func(t3 T3) func(T1, T2, T4) R { + return func(t1 T1, t2 T2, t4 T4) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore3of4 takes a function with 3 parameters and returns a new function with 4 parameters that will ignore the values at positions [3] and pass the remaining 3 parameters to the original function +func Ignore3of4[T3 any, F ~func(T1, T2, T4) R, T1, T2, T4, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t1, t2, t4) + } +} + +// Bind4of4 takes a function with 4 parameters and returns a new function with 1 parameters that will bind these parameters to the positions [4] of the original function. +// The return value of is a function with the remaining 3 parameters at positions [1, 2, 3] of the original function. +func Bind4of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T4) func(T1, T2, T3) R { + return func(t4 T4) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore4of4 takes a function with 3 parameters and returns a new function with 4 parameters that will ignore the values at positions [4] and pass the remaining 3 parameters to the original function +func Ignore4of4[T4 any, F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t1, t2, t3) + } +} + +// Bind12of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 2] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [3, 4] of the original function. +func Bind12of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T2) func(T3, T4) R { + return func(t1 T1, t2 T2) func(T3, T4) R { + return func(t3 T3, t4 T4) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore12of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 2] and pass the remaining 2 parameters to the original function +func Ignore12of4[T1, T2 any, F ~func(T3, T4) R, T3, T4, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t3, t4) + } +} + +// Bind13of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 3] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [2, 4] of the original function. +func Bind13of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T3) func(T2, T4) R { + return func(t1 T1, t3 T3) func(T2, T4) R { + return func(t2 T2, t4 T4) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore13of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 3] and pass the remaining 2 parameters to the original function +func Ignore13of4[T1, T3 any, F ~func(T2, T4) R, T2, T4, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t2, t4) + } +} + +// Bind14of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [1, 4] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [2, 3] of the original function. +func Bind14of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T4) func(T2, T3) R { + return func(t1 T1, t4 T4) func(T2, T3) R { + return func(t2 T2, t3 T3) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore14of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 4] and pass the remaining 2 parameters to the original function +func Ignore14of4[T1, T4 any, F ~func(T2, T3) R, T2, T3, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t2, t3) + } +} + +// Bind23of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [2, 3] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [1, 4] of the original function. +func Bind23of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T2, T3) func(T1, T4) R { + return func(t2 T2, t3 T3) func(T1, T4) R { + return func(t1 T1, t4 T4) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore23of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [2, 3] and pass the remaining 2 parameters to the original function +func Ignore23of4[T2, T3 any, F ~func(T1, T4) R, T1, T4, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t1, t4) + } +} + +// Bind24of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [2, 4] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [1, 3] of the original function. +func Bind24of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T2, T4) func(T1, T3) R { + return func(t2 T2, t4 T4) func(T1, T3) R { + return func(t1 T1, t3 T3) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore24of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [2, 4] and pass the remaining 2 parameters to the original function +func Ignore24of4[T2, T4 any, F ~func(T1, T3) R, T1, T3, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t1, t3) + } +} + +// Bind34of4 takes a function with 4 parameters and returns a new function with 2 parameters that will bind these parameters to the positions [3, 4] of the original function. +// The return value of is a function with the remaining 2 parameters at positions [1, 2] of the original function. +func Bind34of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T3, T4) func(T1, T2) R { + return func(t3 T3, t4 T4) func(T1, T2) R { + return func(t1 T1, t2 T2) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore34of4 takes a function with 2 parameters and returns a new function with 4 parameters that will ignore the values at positions [3, 4] and pass the remaining 2 parameters to the original function +func Ignore34of4[T3, T4 any, F ~func(T1, T2) R, T1, T2, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t1, t2) + } +} + +// Bind123of4 takes a function with 4 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [1, 2, 3] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [4] of the original function. +func Bind123of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T2, T3) func(T4) R { + return func(t1 T1, t2 T2, t3 T3) func(T4) R { + return func(t4 T4) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore123of4 takes a function with 1 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 2, 3] and pass the remaining 1 parameters to the original function +func Ignore123of4[T1, T2, T3 any, F ~func(T4) R, T4, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t4) + } +} + +// Bind124of4 takes a function with 4 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [1, 2, 4] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [3] of the original function. +func Bind124of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T2, T4) func(T3) R { + return func(t1 T1, t2 T2, t4 T4) func(T3) R { + return func(t3 T3) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore124of4 takes a function with 1 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 2, 4] and pass the remaining 1 parameters to the original function +func Ignore124of4[T1, T2, T4 any, F ~func(T3) R, T3, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t3) + } +} + +// Bind134of4 takes a function with 4 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [1, 3, 4] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [2] of the original function. +func Bind134of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T3, T4) func(T2) R { + return func(t1 T1, t3 T3, t4 T4) func(T2) R { + return func(t2 T2) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore134of4 takes a function with 1 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 3, 4] and pass the remaining 1 parameters to the original function +func Ignore134of4[T1, T3, T4 any, F ~func(T2) R, T2, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t2) + } +} + +// Bind234of4 takes a function with 4 parameters and returns a new function with 3 parameters that will bind these parameters to the positions [2, 3, 4] of the original function. +// The return value of is a function with the remaining 1 parameters at positions [1] of the original function. +func Bind234of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T2, T3, T4) func(T1) R { + return func(t2 T2, t3 T3, t4 T4) func(T1) R { + return func(t1 T1) R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore234of4 takes a function with 1 parameters and returns a new function with 4 parameters that will ignore the values at positions [2, 3, 4] and pass the remaining 1 parameters to the original function +func Ignore234of4[T2, T3, T4 any, F ~func(T1) R, T1, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(t1) + } +} + +// Bind1234of4 takes a function with 4 parameters and returns a new function with 4 parameters that will bind these parameters to the positions [1, 2, 3, 4] of the original function. +// The return value of is a function with the remaining 0 parameters at positions [] of the original function. +func Bind1234of4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) func() R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) func() R { + return func() R { + return f(t1, t2, t3, t4) + } + } +} + +// Ignore1234of4 takes a function with 0 parameters and returns a new function with 4 parameters that will ignore the values at positions [1, 2, 3, 4] and pass the remaining 0 parameters to the original function +func Ignore1234of4[T1, T2, T3, T4 any, F ~func() R, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f() + } +} diff --git a/v2/function/cache.go b/v2/function/cache.go new file mode 100644 index 0000000..70aaa1f --- /dev/null +++ b/v2/function/cache.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +import ( + G "github.com/IBM/fp-go/v2/function/generic" +) + +// Memoize converts a unary function into a unary function that caches the value depending on the parameter +func Memoize[K comparable, T any](f func(K) T) func(K) T { + return G.Memoize(f) +} + +// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter +func ContramapMemoize[T, A any, K comparable](kf func(A) K) func(func(A) T) func(A) T { + return G.ContramapMemoize[func(A) T](kf) +} + +// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter +func CacheCallback[ + T, A any, K comparable](kf func(A) K, getOrCreate func(K, func() func() T) func() T) func(func(A) T) func(A) T { + return G.CacheCallback[func(func(A) T) func(A) T](kf, getOrCreate) +} + +// SingleElementCache creates a cache function for use with the [CacheCallback] method that has a maximum capacity of one single item +func SingleElementCache[K comparable, T any]() func(K, func() func() T) func() T { + return G.SingleElementCache[func() func() T, K]() +} diff --git a/v2/function/cache_test.go b/v2/function/cache_test.go new file mode 100644 index 0000000..37acbb0 --- /dev/null +++ b/v2/function/cache_test.go @@ -0,0 +1,70 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCache(t *testing.T) { + var count int + + withSideEffect := func(n int) int { + count++ + return n + } + + cached := Memoize(withSideEffect) + + assert.Equal(t, 0, count) + + assert.Equal(t, 10, cached(10)) + assert.Equal(t, 1, count) + + assert.Equal(t, 10, cached(10)) + assert.Equal(t, 1, count) + + assert.Equal(t, 20, cached(20)) + assert.Equal(t, 2, count) + + assert.Equal(t, 20, cached(20)) + assert.Equal(t, 2, count) + + assert.Equal(t, 10, cached(10)) + assert.Equal(t, 2, count) +} + +func TestSingleElementCache(t *testing.T) { + f := func(key string) string { + return fmt.Sprintf("%s: %d", key, rand.Int()) + } + cb := CacheCallback(func(s string) string { return s }, SingleElementCache[string, string]()) + cf := cb(f) + + v1 := cf("1") + v2 := cf("1") + v3 := cf("2") + v4 := cf("1") + + assert.Equal(t, v1, v2) + assert.NotEqual(t, v2, v3) + assert.NotEqual(t, v3, v4) + assert.NotEqual(t, v1, v4) +} diff --git a/v2/function/constants.go b/v2/function/constants.go new file mode 100644 index 0000000..b5927cd --- /dev/null +++ b/v2/function/constants.go @@ -0,0 +1,24 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +var ConstTrue = Constant(true) +var ConstFalse = Constant(false) + +// ConstNil returns nil +func ConstNil[A any]() *A { + return (*A)(nil) +} diff --git a/v2/function/doc.go b/v2/function/doc.go new file mode 100644 index 0000000..4ce17a6 --- /dev/null +++ b/v2/function/doc.go @@ -0,0 +1,21 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package function implements function composition primitives, most prominently [Pipe2] and [Flow2] +package function + +//go:generate go run .. pipe --count 20 --filename gen.go + +//go:generate go run .. bind --count 5 --filename binds.go diff --git a/v2/function/flip.go b/v2/function/flip.go new file mode 100644 index 0000000..a721924 --- /dev/null +++ b/v2/function/flip.go @@ -0,0 +1,25 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +// Flip reverses the order of parameters of a curried function +func Flip[T1, T2, R any](f func(T1) func(T2) R) func(T2) func(T1) R { + return func(t2 T2) func(T1) R { + return func(t1 T1) R { + return f(t1)(t2) + } + } +} diff --git a/v2/function/flip_test.go b/v2/function/flip_test.go new file mode 100644 index 0000000..55b0c27 --- /dev/null +++ b/v2/function/flip_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFlip(t *testing.T) { + + x := Curry2(func(a, b string) string { + return fmt.Sprintf("%s:%s", a, b) + }) + + assert.Equal(t, "a:b", x("a")("b")) + + y := Flip(x) + + assert.Equal(t, "b:a", y("a")("b")) +} diff --git a/v2/function/function.go b/v2/function/function.go new file mode 100644 index 0000000..e041ac7 --- /dev/null +++ b/v2/function/function.go @@ -0,0 +1,68 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +// Identity returns the value 'a' +func Identity[A any](a A) A { + return a +} + +// Constant creates a nullary function that returns the constant value 'a' +func Constant[A any](a A) func() A { + return func() A { + return a + } +} + +// Constant1 creates a unary function that returns the constant value 'a' and ignores its input +func Constant1[B, A any](a A) func(B) A { + return func(_ B) A { + return a + } +} + +// Constant2 creates a binary function that returns the constant value 'a' and ignores its inputs +func Constant2[B, C, A any](a A) func(B, C) A { + return func(_ B, _ C) A { + return a + } +} + +func IsNil[A any](a *A) bool { + return a == nil +} + +func IsNonNil[A any](a *A) bool { + return a != nil +} + +// Swap returns a new binary function that changes the order of input parameters +func Swap[T1, T2, R any](f func(T1, T2) R) func(T2, T1) R { + return func(t2 T2, t1 T1) R { + return f(t1, t2) + } +} + +// First returns the first out of two input values +func First[T1, T2 any](t1 T1, _ T2) T1 { + return t1 +} + +// Second returns the second out of two input values +// Identical to [SK] +func Second[T1, T2 any](_ T1, t2 T2) T2 { + return t2 +} diff --git a/v2/function/gen.go b/v2/function/gen.go new file mode 100644 index 0000000..de5766a --- /dev/null +++ b/v2/function/gen.go @@ -0,0 +1,1572 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:18:53.6664566 +0100 CET m=+0.011085101 + +package function + +// Pipe0 takes an initial value t0 and successively applies 0 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe0[T0 any](t0 T0) T0 { + return t0 +} + +// Variadic0 converts a function taking 0 parameters and a final slice into a function with 0 parameters but a final variadic argument +func Variadic0[V, R any](f func([]V) R) func(...V) R { + return func(v ...V) R { + return f(v) + } +} + +// Unvariadic0 converts a function taking 0 parameters and a final variadic argument into a function with 0 parameters but a final slice argument +func Unvariadic0[V, R any](f func(...V) R) func([]V) R { + return func(v []V) R { + return f(v...) + } +} + +// Unsliced0 converts a function taking a slice parameter into a function with 0 parameters +func Unsliced0[F ~func([]T) R, T, R any](f F) func() R { + return func() R { + return f([]T{}) + } +} + +// Pipe1 takes an initial value t0 and successively applies 1 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe1[F1 ~func(T0) T1, T0, T1 any](t0 T0, f1 F1) T1 { + return f1(t0) +} + +// Flow1 creates a function that takes an initial value t0 and successively applies 1 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow1[F1 ~func(T0) T1, T0, T1 any](f1 F1) func(T0) T1 { + return func(t0 T0) T1 { + return Pipe1(t0, f1) + } +} + +// Nullary1 creates a parameter less function from a parameter less function and 0 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary1[F1 ~func() T1, T1 any](f1 F1) func() T1 { + return func() T1 { + return Pipe0(f1()) + } +} + +// Curry1 takes a function with 1 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry1] +func Curry1[FCT ~func(T0) T1, T0, T1 any](f FCT) func(T0) T1 { + return func(t0 T0) T1 { + return f(t0) + } +} + +// Uncurry1 takes a cascade of 1 functions each taking only one parameter and returns a function with 1 parameters . +// The inverse function is [Curry1] +func Uncurry1[FCT ~func(T0) T1, T0, T1 any](f FCT) func(T0) T1 { + return func(t0 T0) T1 { + return f(t0) + } +} + +// Variadic1 converts a function taking 1 parameters and a final slice into a function with 1 parameters but a final variadic argument +func Variadic1[T1, V, R any](f func(T1, []V) R) func(T1, ...V) R { + return func(t1 T1, v ...V) R { + return f(t1, v) + } +} + +// Unvariadic1 converts a function taking 1 parameters and a final variadic argument into a function with 1 parameters but a final slice argument +func Unvariadic1[T1, V, R any](f func(T1, ...V) R) func(T1, []V) R { + return func(t1 T1, v []V) R { + return f(t1, v...) + } +} + +// Unsliced1 converts a function taking a slice parameter into a function with 1 parameters +func Unsliced1[F ~func([]T) R, T, R any](f F) func(T) R { + return func(t1 T) R { + return f([]T{t1}) + } +} + +// Pipe2 takes an initial value t0 and successively applies 2 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe2[F1 ~func(T0) T1, F2 ~func(T1) T2, T0, T1, T2 any](t0 T0, f1 F1, f2 F2) T2 { + return f2(f1(t0)) +} + +// Flow2 creates a function that takes an initial value t0 and successively applies 2 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow2[F1 ~func(T0) T1, F2 ~func(T1) T2, T0, T1, T2 any](f1 F1, f2 F2) func(T0) T2 { + return func(t0 T0) T2 { + return Pipe2(t0, f1, f2) + } +} + +// Nullary2 creates a parameter less function from a parameter less function and 1 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary2[F1 ~func() T1, F2 ~func(T1) T2, T1, T2 any](f1 F1, f2 F2) func() T2 { + return func() T2 { + return Pipe1(f1(), f2) + } +} + +// Curry2 takes a function with 2 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry2] +func Curry2[FCT ~func(T0, T1) T2, T0, T1, T2 any](f FCT) func(T0) func(T1) T2 { + return func(t0 T0) func(t1 T1) T2 { + return func(t1 T1) T2 { + return f(t0, t1) + } + } +} + +// Uncurry2 takes a cascade of 2 functions each taking only one parameter and returns a function with 2 parameters . +// The inverse function is [Curry2] +func Uncurry2[FCT ~func(T0) func(T1) T2, T0, T1, T2 any](f FCT) func(T0, T1) T2 { + return func(t0 T0, t1 T1) T2 { + return f(t0)(t1) + } +} + +// Variadic2 converts a function taking 2 parameters and a final slice into a function with 2 parameters but a final variadic argument +func Variadic2[T1, T2, V, R any](f func(T1, T2, []V) R) func(T1, T2, ...V) R { + return func(t1 T1, t2 T2, v ...V) R { + return f(t1, t2, v) + } +} + +// Unvariadic2 converts a function taking 2 parameters and a final variadic argument into a function with 2 parameters but a final slice argument +func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) R) func(T1, T2, []V) R { + return func(t1 T1, t2 T2, v []V) R { + return f(t1, t2, v...) + } +} + +// Unsliced2 converts a function taking a slice parameter into a function with 2 parameters +func Unsliced2[F ~func([]T) R, T, R any](f F) func(T, T) R { + return func(t1, t2 T) R { + return f([]T{t1, t2}) + } +} + +// Pipe3 takes an initial value t0 and successively applies 3 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe3[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T0, T1, T2, T3 any](t0 T0, f1 F1, f2 F2, f3 F3) T3 { + return f3(f2(f1(t0))) +} + +// Flow3 creates a function that takes an initial value t0 and successively applies 3 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow3[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T0, T1, T2, T3 any](f1 F1, f2 F2, f3 F3) func(T0) T3 { + return func(t0 T0) T3 { + return Pipe3(t0, f1, f2, f3) + } +} + +// Nullary3 creates a parameter less function from a parameter less function and 2 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary3[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, T1, T2, T3 any](f1 F1, f2 F2, f3 F3) func() T3 { + return func() T3 { + return Pipe2(f1(), f2, f3) + } +} + +// Curry3 takes a function with 3 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry3] +func Curry3[FCT ~func(T0, T1, T2) T3, T0, T1, T2, T3 any](f FCT) func(T0) func(T1) func(T2) T3 { + return func(t0 T0) func(t1 T1) func(t2 T2) T3 { + return func(t1 T1) func(t2 T2) T3 { + return func(t2 T2) T3 { + return f(t0, t1, t2) + } + } + } +} + +// Uncurry3 takes a cascade of 3 functions each taking only one parameter and returns a function with 3 parameters . +// The inverse function is [Curry3] +func Uncurry3[FCT ~func(T0) func(T1) func(T2) T3, T0, T1, T2, T3 any](f FCT) func(T0, T1, T2) T3 { + return func(t0 T0, t1 T1, t2 T2) T3 { + return f(t0)(t1)(t2) + } +} + +// Variadic3 converts a function taking 3 parameters and a final slice into a function with 3 parameters but a final variadic argument +func Variadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, []V) R) func(T1, T2, T3, ...V) R { + return func(t1 T1, t2 T2, t3 T3, v ...V) R { + return f(t1, t2, t3, v) + } +} + +// Unvariadic3 converts a function taking 3 parameters and a final variadic argument into a function with 3 parameters but a final slice argument +func Unvariadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, ...V) R) func(T1, T2, T3, []V) R { + return func(t1 T1, t2 T2, t3 T3, v []V) R { + return f(t1, t2, t3, v...) + } +} + +// Unsliced3 converts a function taking a slice parameter into a function with 3 parameters +func Unsliced3[F ~func([]T) R, T, R any](f F) func(T, T, T) R { + return func(t1, t2, t3 T) R { + return f([]T{t1, t2, t3}) + } +} + +// Pipe4 takes an initial value t0 and successively applies 4 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe4[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, T0, T1, T2, T3, T4 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4) T4 { + return f4(f3(f2(f1(t0)))) +} + +// Flow4 creates a function that takes an initial value t0 and successively applies 4 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow4[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, T0, T1, T2, T3, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T0) T4 { + return func(t0 T0) T4 { + return Pipe4(t0, f1, f2, f3, f4) + } +} + +// Nullary4 creates a parameter less function from a parameter less function and 3 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary4[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, T1, T2, T3, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func() T4 { + return func() T4 { + return Pipe3(f1(), f2, f3, f4) + } +} + +// Curry4 takes a function with 4 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry4] +func Curry4[FCT ~func(T0, T1, T2, T3) T4, T0, T1, T2, T3, T4 any](f FCT) func(T0) func(T1) func(T2) func(T3) T4 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) T4 { + return func(t1 T1) func(t2 T2) func(t3 T3) T4 { + return func(t2 T2) func(t3 T3) T4 { + return func(t3 T3) T4 { + return f(t0, t1, t2, t3) + } + } + } + } +} + +// Uncurry4 takes a cascade of 4 functions each taking only one parameter and returns a function with 4 parameters . +// The inverse function is [Curry4] +func Uncurry4[FCT ~func(T0) func(T1) func(T2) func(T3) T4, T0, T1, T2, T3, T4 any](f FCT) func(T0, T1, T2, T3) T4 { + return func(t0 T0, t1 T1, t2 T2, t3 T3) T4 { + return f(t0)(t1)(t2)(t3) + } +} + +// Variadic4 converts a function taking 4 parameters and a final slice into a function with 4 parameters but a final variadic argument +func Variadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, []V) R) func(T1, T2, T3, T4, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, v ...V) R { + return f(t1, t2, t3, t4, v) + } +} + +// Unvariadic4 converts a function taking 4 parameters and a final variadic argument into a function with 4 parameters but a final slice argument +func Unvariadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, ...V) R) func(T1, T2, T3, T4, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, v []V) R { + return f(t1, t2, t3, t4, v...) + } +} + +// Unsliced4 converts a function taking a slice parameter into a function with 4 parameters +func Unsliced4[F ~func([]T) R, T, R any](f F) func(T, T, T, T) R { + return func(t1, t2, t3, t4 T) R { + return f([]T{t1, t2, t3, t4}) + } +} + +// Pipe5 takes an initial value t0 and successively applies 5 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe5[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, T0, T1, T2, T3, T4, T5 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) T5 { + return f5(f4(f3(f2(f1(t0))))) +} + +// Flow5 creates a function that takes an initial value t0 and successively applies 5 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow5[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, T0, T1, T2, T3, T4, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T0) T5 { + return func(t0 T0) T5 { + return Pipe5(t0, f1, f2, f3, f4, f5) + } +} + +// Nullary5 creates a parameter less function from a parameter less function and 4 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary5[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, T1, T2, T3, T4, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func() T5 { + return func() T5 { + return Pipe4(f1(), f2, f3, f4, f5) + } +} + +// Curry5 takes a function with 5 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry5] +func Curry5[FCT ~func(T0, T1, T2, T3, T4) T5, T0, T1, T2, T3, T4, T5 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) T5 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) T5 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) T5 { + return func(t2 T2) func(t3 T3) func(t4 T4) T5 { + return func(t3 T3) func(t4 T4) T5 { + return func(t4 T4) T5 { + return f(t0, t1, t2, t3, t4) + } + } + } + } + } +} + +// Uncurry5 takes a cascade of 5 functions each taking only one parameter and returns a function with 5 parameters . +// The inverse function is [Curry5] +func Uncurry5[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) T5, T0, T1, T2, T3, T4, T5 any](f FCT) func(T0, T1, T2, T3, T4) T5 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) T5 { + return f(t0)(t1)(t2)(t3)(t4) + } +} + +// Variadic5 converts a function taking 5 parameters and a final slice into a function with 5 parameters but a final variadic argument +func Variadic5[T1, T2, T3, T4, T5, V, R any](f func(T1, T2, T3, T4, T5, []V) R) func(T1, T2, T3, T4, T5, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, v ...V) R { + return f(t1, t2, t3, t4, t5, v) + } +} + +// Unvariadic5 converts a function taking 5 parameters and a final variadic argument into a function with 5 parameters but a final slice argument +func Unvariadic5[T1, T2, T3, T4, T5, V, R any](f func(T1, T2, T3, T4, T5, ...V) R) func(T1, T2, T3, T4, T5, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, v []V) R { + return f(t1, t2, t3, t4, t5, v...) + } +} + +// Unsliced5 converts a function taking a slice parameter into a function with 5 parameters +func Unsliced5[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5 T) R { + return f([]T{t1, t2, t3, t4, t5}) + } +} + +// Pipe6 takes an initial value t0 and successively applies 6 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe6[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) T6 { + return f6(f5(f4(f3(f2(f1(t0)))))) +} + +// Flow6 creates a function that takes an initial value t0 and successively applies 6 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow6[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T0) T6 { + return func(t0 T0) T6 { + return Pipe6(t0, f1, f2, f3, f4, f5, f6) + } +} + +// Nullary6 creates a parameter less function from a parameter less function and 5 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary6[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, T1, T2, T3, T4, T5, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func() T6 { + return func() T6 { + return Pipe5(f1(), f2, f3, f4, f5, f6) + } +} + +// Curry6 takes a function with 6 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry6] +func Curry6[FCT ~func(T0, T1, T2, T3, T4, T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) T6 { + return func(t3 T3) func(t4 T4) func(t5 T5) T6 { + return func(t4 T4) func(t5 T5) T6 { + return func(t5 T5) T6 { + return f(t0, t1, t2, t3, t4, t5) + } + } + } + } + } + } +} + +// Uncurry6 takes a cascade of 6 functions each taking only one parameter and returns a function with 6 parameters . +// The inverse function is [Curry6] +func Uncurry6[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) T6, T0, T1, T2, T3, T4, T5, T6 any](f FCT) func(T0, T1, T2, T3, T4, T5) T6 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) T6 { + return f(t0)(t1)(t2)(t3)(t4)(t5) + } +} + +// Variadic6 converts a function taking 6 parameters and a final slice into a function with 6 parameters but a final variadic argument +func Variadic6[T1, T2, T3, T4, T5, T6, V, R any](f func(T1, T2, T3, T4, T5, T6, []V) R) func(T1, T2, T3, T4, T5, T6, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, v) + } +} + +// Unvariadic6 converts a function taking 6 parameters and a final variadic argument into a function with 6 parameters but a final slice argument +func Unvariadic6[T1, T2, T3, T4, T5, T6, V, R any](f func(T1, T2, T3, T4, T5, T6, ...V) R) func(T1, T2, T3, T4, T5, T6, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, v []V) R { + return f(t1, t2, t3, t4, t5, t6, v...) + } +} + +// Unsliced6 converts a function taking a slice parameter into a function with 6 parameters +func Unsliced6[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6 T) R { + return f([]T{t1, t2, t3, t4, t5, t6}) + } +} + +// Pipe7 takes an initial value t0 and successively applies 7 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe7[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) T7 { + return f7(f6(f5(f4(f3(f2(f1(t0))))))) +} + +// Flow7 creates a function that takes an initial value t0 and successively applies 7 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow7[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T0) T7 { + return func(t0 T0) T7 { + return Pipe7(t0, f1, f2, f3, f4, f5, f6, f7) + } +} + +// Nullary7 creates a parameter less function from a parameter less function and 6 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary7[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, T1, T2, T3, T4, T5, T6, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func() T7 { + return func() T7 { + return Pipe6(f1(), f2, f3, f4, f5, f6, f7) + } +} + +// Curry7 takes a function with 7 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry7] +func Curry7[FCT ~func(T0, T1, T2, T3, T4, T5, T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) T7 { + return func(t4 T4) func(t5 T5) func(t6 T6) T7 { + return func(t5 T5) func(t6 T6) T7 { + return func(t6 T6) T7 { + return f(t0, t1, t2, t3, t4, t5, t6) + } + } + } + } + } + } + } +} + +// Uncurry7 takes a cascade of 7 functions each taking only one parameter and returns a function with 7 parameters . +// The inverse function is [Curry7] +func Uncurry7[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T7, T0, T1, T2, T3, T4, T5, T6, T7 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6) T7 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) T7 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6) + } +} + +// Variadic7 converts a function taking 7 parameters and a final slice into a function with 7 parameters but a final variadic argument +func Variadic7[T1, T2, T3, T4, T5, T6, T7, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, []V) R) func(T1, T2, T3, T4, T5, T6, T7, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, v) + } +} + +// Unvariadic7 converts a function taking 7 parameters and a final variadic argument into a function with 7 parameters but a final slice argument +func Unvariadic7[T1, T2, T3, T4, T5, T6, T7, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, v...) + } +} + +// Unsliced7 converts a function taking a slice parameter into a function with 7 parameters +func Unsliced7[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7}) + } +} + +// Pipe8 takes an initial value t0 and successively applies 8 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe8[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) T8 { + return f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))) +} + +// Flow8 creates a function that takes an initial value t0 and successively applies 8 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow8[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T0) T8 { + return func(t0 T0) T8 { + return Pipe8(t0, f1, f2, f3, f4, f5, f6, f7, f8) + } +} + +// Nullary8 creates a parameter less function from a parameter less function and 7 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary8[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, T1, T2, T3, T4, T5, T6, T7, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func() T8 { + return func() T8 { + return Pipe7(f1(), f2, f3, f4, f5, f6, f7, f8) + } +} + +// Curry8 takes a function with 8 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry8] +func Curry8[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) T8 { + return func(t5 T5) func(t6 T6) func(t7 T7) T8 { + return func(t6 T6) func(t7 T7) T8 { + return func(t7 T7) T8 { + return f(t0, t1, t2, t3, t4, t5, t6, t7) + } + } + } + } + } + } + } + } +} + +// Uncurry8 takes a cascade of 8 functions each taking only one parameter and returns a function with 8 parameters . +// The inverse function is [Curry8] +func Uncurry8[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T8, T0, T1, T2, T3, T4, T5, T6, T7, T8 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7) T8 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) T8 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7) + } +} + +// Variadic8 converts a function taking 8 parameters and a final slice into a function with 8 parameters but a final variadic argument +func Variadic8[T1, T2, T3, T4, T5, T6, T7, T8, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, v) + } +} + +// Unvariadic8 converts a function taking 8 parameters and a final variadic argument into a function with 8 parameters but a final slice argument +func Unvariadic8[T1, T2, T3, T4, T5, T6, T7, T8, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, v...) + } +} + +// Unsliced8 converts a function taking a slice parameter into a function with 8 parameters +func Unsliced8[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8}) + } +} + +// Pipe9 takes an initial value t0 and successively applies 9 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe9[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) T9 { + return f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))) +} + +// Flow9 creates a function that takes an initial value t0 and successively applies 9 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow9[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T0) T9 { + return func(t0 T0) T9 { + return Pipe9(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9) + } +} + +// Nullary9 creates a parameter less function from a parameter less function and 8 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary9[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func() T9 { + return func() T9 { + return Pipe8(f1(), f2, f3, f4, f5, f6, f7, f8, f9) + } +} + +// Curry9 takes a function with 9 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry9] +func Curry9[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) T9 { + return func(t6 T6) func(t7 T7) func(t8 T8) T9 { + return func(t7 T7) func(t8 T8) T9 { + return func(t8 T8) T9 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8) + } + } + } + } + } + } + } + } + } +} + +// Uncurry9 takes a cascade of 9 functions each taking only one parameter and returns a function with 9 parameters . +// The inverse function is [Curry9] +func Uncurry9[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T9, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) T9 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) T9 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8) + } +} + +// Variadic9 converts a function taking 9 parameters and a final slice into a function with 9 parameters but a final variadic argument +func Variadic9[T1, T2, T3, T4, T5, T6, T7, T8, T9, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, v) + } +} + +// Unvariadic9 converts a function taking 9 parameters and a final variadic argument into a function with 9 parameters but a final slice argument +func Unvariadic9[T1, T2, T3, T4, T5, T6, T7, T8, T9, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, v...) + } +} + +// Unsliced9 converts a function taking a slice parameter into a function with 9 parameters +func Unsliced9[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9}) + } +} + +// Pipe10 takes an initial value t0 and successively applies 10 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe10[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) T10 { + return f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))) +} + +// Flow10 creates a function that takes an initial value t0 and successively applies 10 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow10[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T0) T10 { + return func(t0 T0) T10 { + return Pipe10(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) + } +} + +// Nullary10 creates a parameter less function from a parameter less function and 9 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary10[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func() T10 { + return func() T10 { + return Pipe9(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10) + } +} + +// Curry10 takes a function with 10 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry10] +func Curry10[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) T10 { + return func(t7 T7) func(t8 T8) func(t9 T9) T10 { + return func(t8 T8) func(t9 T9) T10 { + return func(t9 T9) T10 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry10 takes a cascade of 10 functions each taking only one parameter and returns a function with 10 parameters . +// The inverse function is [Curry10] +func Uncurry10[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) T10 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) T10 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9) + } +} + +// Variadic10 converts a function taking 10 parameters and a final slice into a function with 10 parameters but a final variadic argument +func Variadic10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, v) + } +} + +// Unvariadic10 converts a function taking 10 parameters and a final variadic argument into a function with 10 parameters but a final slice argument +func Unvariadic10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, v...) + } +} + +// Unsliced10 converts a function taking a slice parameter into a function with 10 parameters +func Unsliced10[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10}) + } +} + +// Pipe11 takes an initial value t0 and successively applies 11 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe11[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) T11 { + return f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))) +} + +// Flow11 creates a function that takes an initial value t0 and successively applies 11 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow11[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) func(T0) T11 { + return func(t0 T0) T11 { + return Pipe11(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) + } +} + +// Nullary11 creates a parameter less function from a parameter less function and 10 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary11[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) func() T11 { + return func() T11 { + return Pipe10(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) + } +} + +// Curry11 takes a function with 11 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry11] +func Curry11[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t8 T8) func(t9 T9) func(t10 T10) T11 { + return func(t9 T9) func(t10 T10) T11 { + return func(t10 T10) T11 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry11 takes a cascade of 11 functions each taking only one parameter and returns a function with 11 parameters . +// The inverse function is [Curry11] +func Uncurry11[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) T11 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) T11 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10) + } +} + +// Variadic11 converts a function taking 11 parameters and a final slice into a function with 11 parameters but a final variadic argument +func Variadic11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, v) + } +} + +// Unvariadic11 converts a function taking 11 parameters and a final variadic argument into a function with 11 parameters but a final slice argument +func Unvariadic11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, v...) + } +} + +// Unsliced11 converts a function taking a slice parameter into a function with 11 parameters +func Unsliced11[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11}) + } +} + +// Pipe12 takes an initial value t0 and successively applies 12 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe12[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) T12 { + return f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))) +} + +// Flow12 creates a function that takes an initial value t0 and successively applies 12 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow12[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) func(T0) T12 { + return func(t0 T0) T12 { + return Pipe12(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12) + } +} + +// Nullary12 creates a parameter less function from a parameter less function and 11 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary12[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) func() T12 { + return func() T12 { + return Pipe11(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12) + } +} + +// Curry12 takes a function with 12 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry12] +func Curry12[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t9 T9) func(t10 T10) func(t11 T11) T12 { + return func(t10 T10) func(t11 T11) T12 { + return func(t11 T11) T12 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry12 takes a cascade of 12 functions each taking only one parameter and returns a function with 12 parameters . +// The inverse function is [Curry12] +func Uncurry12[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) T12 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) T12 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11) + } +} + +// Variadic12 converts a function taking 12 parameters and a final slice into a function with 12 parameters but a final variadic argument +func Variadic12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, v) + } +} + +// Unvariadic12 converts a function taking 12 parameters and a final variadic argument into a function with 12 parameters but a final slice argument +func Unvariadic12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, v...) + } +} + +// Unsliced12 converts a function taking a slice parameter into a function with 12 parameters +func Unsliced12[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12}) + } +} + +// Pipe13 takes an initial value t0 and successively applies 13 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe13[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) T13 { + return f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))) +} + +// Flow13 creates a function that takes an initial value t0 and successively applies 13 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow13[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) func(T0) T13 { + return func(t0 T0) T13 { + return Pipe13(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13) + } +} + +// Nullary13 creates a parameter less function from a parameter less function and 12 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary13[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) func() T13 { + return func() T13 { + return Pipe12(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13) + } +} + +// Curry13 takes a function with 13 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry13] +func Curry13[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t10 T10) func(t11 T11) func(t12 T12) T13 { + return func(t11 T11) func(t12 T12) T13 { + return func(t12 T12) T13 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry13 takes a cascade of 13 functions each taking only one parameter and returns a function with 13 parameters . +// The inverse function is [Curry13] +func Uncurry13[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) T13 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) T13 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12) + } +} + +// Variadic13 converts a function taking 13 parameters and a final slice into a function with 13 parameters but a final variadic argument +func Variadic13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, v) + } +} + +// Unvariadic13 converts a function taking 13 parameters and a final variadic argument into a function with 13 parameters but a final slice argument +func Unvariadic13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, v...) + } +} + +// Unsliced13 converts a function taking a slice parameter into a function with 13 parameters +func Unsliced13[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13}) + } +} + +// Pipe14 takes an initial value t0 and successively applies 14 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe14[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) T14 { + return f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))) +} + +// Flow14 creates a function that takes an initial value t0 and successively applies 14 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow14[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) func(T0) T14 { + return func(t0 T0) T14 { + return Pipe14(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14) + } +} + +// Nullary14 creates a parameter less function from a parameter less function and 13 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary14[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) func() T14 { + return func() T14 { + return Pipe13(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14) + } +} + +// Curry14 takes a function with 14 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry14] +func Curry14[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t11 T11) func(t12 T12) func(t13 T13) T14 { + return func(t12 T12) func(t13 T13) T14 { + return func(t13 T13) T14 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry14 takes a cascade of 14 functions each taking only one parameter and returns a function with 14 parameters . +// The inverse function is [Curry14] +func Uncurry14[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) T14 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) T14 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13) + } +} + +// Variadic14 converts a function taking 14 parameters and a final slice into a function with 14 parameters but a final variadic argument +func Variadic14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, v) + } +} + +// Unvariadic14 converts a function taking 14 parameters and a final variadic argument into a function with 14 parameters but a final slice argument +func Unvariadic14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, v...) + } +} + +// Unsliced14 converts a function taking a slice parameter into a function with 14 parameters +func Unsliced14[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14}) + } +} + +// Pipe15 takes an initial value t0 and successively applies 15 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe15[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) T15 { + return f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))) +} + +// Flow15 creates a function that takes an initial value t0 and successively applies 15 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow15[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) func(T0) T15 { + return func(t0 T0) T15 { + return Pipe15(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15) + } +} + +// Nullary15 creates a parameter less function from a parameter less function and 14 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary15[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) func() T15 { + return func() T15 { + return Pipe14(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15) + } +} + +// Curry15 takes a function with 15 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry15] +func Curry15[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t12 T12) func(t13 T13) func(t14 T14) T15 { + return func(t13 T13) func(t14 T14) T15 { + return func(t14 T14) T15 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry15 takes a cascade of 15 functions each taking only one parameter and returns a function with 15 parameters . +// The inverse function is [Curry15] +func Uncurry15[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) T15 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) T15 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14) + } +} + +// Variadic15 converts a function taking 15 parameters and a final slice into a function with 15 parameters but a final variadic argument +func Variadic15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, v) + } +} + +// Unvariadic15 converts a function taking 15 parameters and a final variadic argument into a function with 15 parameters but a final slice argument +func Unvariadic15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, v...) + } +} + +// Unsliced15 converts a function taking a slice parameter into a function with 15 parameters +func Unsliced15[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15}) + } +} + +// Pipe16 takes an initial value t0 and successively applies 16 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe16[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16) T16 { + return f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))) +} + +// Flow16 creates a function that takes an initial value t0 and successively applies 16 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow16[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16) func(T0) T16 { + return func(t0 T0) T16 { + return Pipe16(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16) + } +} + +// Nullary16 creates a parameter less function from a parameter less function and 15 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary16[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16) func() T16 { + return func() T16 { + return Pipe15(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16) + } +} + +// Curry16 takes a function with 16 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry16] +func Curry16[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t13 T13) func(t14 T14) func(t15 T15) T16 { + return func(t14 T14) func(t15 T15) T16 { + return func(t15 T15) T16 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry16 takes a cascade of 16 functions each taking only one parameter and returns a function with 16 parameters . +// The inverse function is [Curry16] +func Uncurry16[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) T16 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15) T16 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15) + } +} + +// Variadic16 converts a function taking 16 parameters and a final slice into a function with 16 parameters but a final variadic argument +func Variadic16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, v) + } +} + +// Unvariadic16 converts a function taking 16 parameters and a final variadic argument into a function with 16 parameters but a final slice argument +func Unvariadic16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, v...) + } +} + +// Unsliced16 converts a function taking a slice parameter into a function with 16 parameters +func Unsliced16[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16}) + } +} + +// Pipe17 takes an initial value t0 and successively applies 17 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe17[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17) T17 { + return f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))))) +} + +// Flow17 creates a function that takes an initial value t0 and successively applies 17 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow17[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17) func(T0) T17 { + return func(t0 T0) T17 { + return Pipe17(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17) + } +} + +// Nullary17 creates a parameter less function from a parameter less function and 16 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary17[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17) func() T17 { + return func() T17 { + return Pipe16(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17) + } +} + +// Curry17 takes a function with 17 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry17] +func Curry17[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t14 T14) func(t15 T15) func(t16 T16) T17 { + return func(t15 T15) func(t16 T16) T17 { + return func(t16 T16) T17 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry17 takes a cascade of 17 functions each taking only one parameter and returns a function with 17 parameters . +// The inverse function is [Curry17] +func Uncurry17[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) T17, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) T17 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16) T17 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16) + } +} + +// Variadic17 converts a function taking 17 parameters and a final slice into a function with 17 parameters but a final variadic argument +func Variadic17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, v) + } +} + +// Unvariadic17 converts a function taking 17 parameters and a final variadic argument into a function with 17 parameters but a final slice argument +func Unvariadic17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, v...) + } +} + +// Unsliced17 converts a function taking a slice parameter into a function with 17 parameters +func Unsliced17[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17}) + } +} + +// Pipe18 takes an initial value t0 and successively applies 18 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe18[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18) T18 { + return f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))))) +} + +// Flow18 creates a function that takes an initial value t0 and successively applies 18 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow18[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18) func(T0) T18 { + return func(t0 T0) T18 { + return Pipe18(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18) + } +} + +// Nullary18 creates a parameter less function from a parameter less function and 17 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary18[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18) func() T18 { + return func() T18 { + return Pipe17(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18) + } +} + +// Curry18 takes a function with 18 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry18] +func Curry18[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t15 T15) func(t16 T16) func(t17 T17) T18 { + return func(t16 T16) func(t17 T17) T18 { + return func(t17 T17) T18 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry18 takes a cascade of 18 functions each taking only one parameter and returns a function with 18 parameters . +// The inverse function is [Curry18] +func Uncurry18[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) T18, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) T18 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17) T18 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17) + } +} + +// Variadic18 converts a function taking 18 parameters and a final slice into a function with 18 parameters but a final variadic argument +func Variadic18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, v) + } +} + +// Unvariadic18 converts a function taking 18 parameters and a final variadic argument into a function with 18 parameters but a final slice argument +func Unvariadic18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, v...) + } +} + +// Unsliced18 converts a function taking a slice parameter into a function with 18 parameters +func Unsliced18[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18}) + } +} + +// Pipe19 takes an initial value t0 and successively applies 19 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe19[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19) T19 { + return f19(f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0))))))))))))))))))) +} + +// Flow19 creates a function that takes an initial value t0 and successively applies 19 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow19[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19) func(T0) T19 { + return func(t0 T0) T19 { + return Pipe19(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19) + } +} + +// Nullary19 creates a parameter less function from a parameter less function and 18 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary19[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19) func() T19 { + return func() T19 { + return Pipe18(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19) + } +} + +// Curry19 takes a function with 19 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry19] +func Curry19[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t16 T16) func(t17 T17) func(t18 T18) T19 { + return func(t17 T17) func(t18 T18) T19 { + return func(t18 T18) T19 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry19 takes a cascade of 19 functions each taking only one parameter and returns a function with 19 parameters . +// The inverse function is [Curry19] +func Uncurry19[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) T19, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) T19 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18) T19 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)(t18) + } +} + +// Variadic19 converts a function taking 19 parameters and a final slice into a function with 19 parameters but a final variadic argument +func Variadic19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, v) + } +} + +// Unvariadic19 converts a function taking 19 parameters and a final variadic argument into a function with 19 parameters but a final slice argument +func Unvariadic19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, v...) + } +} + +// Unsliced19 converts a function taking a slice parameter into a function with 19 parameters +func Unsliced19[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19}) + } +} + +// Pipe20 takes an initial value t0 and successively applies 20 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Pipe20[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, F20 ~func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](t0 T0, f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19, f20 F20) T20 { + return f20(f19(f18(f17(f16(f15(f14(f13(f12(f11(f10(f9(f8(f7(f6(f5(f4(f3(f2(f1(t0)))))))))))))))))))) +} + +// Flow20 creates a function that takes an initial value t0 and successively applies 20 functions where the input of a function is the return value of the previous function +// The final return value is the result of the last function application +func Flow20[F1 ~func(T0) T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, F20 ~func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19, f20 F20) func(T0) T20 { + return func(t0 T0) T20 { + return Pipe20(t0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20) + } +} + +// Nullary20 creates a parameter less function from a parameter less function and 19 functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions +func Nullary20[F1 ~func() T1, F2 ~func(T1) T2, F3 ~func(T2) T3, F4 ~func(T3) T4, F5 ~func(T4) T5, F6 ~func(T5) T6, F7 ~func(T6) T7, F8 ~func(T7) T8, F9 ~func(T8) T9, F10 ~func(T9) T10, F11 ~func(T10) T11, F12 ~func(T11) T12, F13 ~func(T12) T13, F14 ~func(T13) T14, F15 ~func(T14) T15, F16 ~func(T15) T16, F17 ~func(T16) T17, F18 ~func(T17) T18, F19 ~func(T18) T19, F20 ~func(T19) T20, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15, f16 F16, f17 F17, f18 F18, f19 F19, f20 F20) func() T20 { + return func() T20 { + return Pipe19(f1(), f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20) + } +} + +// Curry20 takes a function with 20 parameters and returns a cascade of functions each taking only one parameter. +// The inverse function is [Uncurry20] +func Curry20[FCT ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f FCT) func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20 { + return func(t0 T0) func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t1 T1) func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t2 T2) func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t3 T3) func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t4 T4) func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t5 T5) func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t6 T6) func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t7 T7) func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t8 T8) func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t9 T9) func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t10 T10) func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t11 T11) func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t12 T12) func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t13 T13) func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t14 T14) func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t15 T15) func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t16 T16) func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t17 T17) func(t18 T18) func(t19 T19) T20 { + return func(t18 T18) func(t19 T19) T20 { + return func(t19 T19) T20 { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19) + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +// Uncurry20 takes a cascade of 20 functions each taking only one parameter and returns a function with 20 parameters . +// The inverse function is [Curry20] +func Uncurry20[FCT ~func(T0) func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) func(T16) func(T17) func(T18) func(T19) T20, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 any](f FCT) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) T20 { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19) T20 { + return f(t0)(t1)(t2)(t3)(t4)(t5)(t6)(t7)(t8)(t9)(t10)(t11)(t12)(t13)(t14)(t15)(t16)(t17)(t18)(t19) + } +} + +// Variadic20 converts a function taking 20 parameters and a final slice into a function with 20 parameters but a final variadic argument +func Variadic20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, []V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, ...V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19, t20 T20, v ...V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, v) + } +} + +// Unvariadic20 converts a function taking 20 parameters and a final variadic argument into a function with 20 parameters but a final slice argument +func Unvariadic20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, V, R any](f func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, ...V) R) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, []V) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16, t17 T17, t18 T18, t19 T19, t20 T20, v []V) R { + return f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, v...) + } +} + +// Unsliced20 converts a function taking a slice parameter into a function with 20 parameters +func Unsliced20[F ~func([]T) R, T, R any](f F) func(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) R { + return func(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20 T) R { + return f([]T{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20}) + } +} diff --git a/v2/function/generic/cache.go b/v2/function/generic/cache.go new file mode 100644 index 0000000..187a31b --- /dev/null +++ b/v2/function/generic/cache.go @@ -0,0 +1,103 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "sync" + + L "github.com/IBM/fp-go/v2/internal/lazy" +) + +// Memoize converts a unary function into a unary function that caches the value depending on the parameter +func Memoize[F ~func(K) T, K comparable, T any](f F) F { + return ContramapMemoize[F](func(k K) K { return k })(f) +} + +// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter +func ContramapMemoize[F ~func(A) T, KF func(A) K, A any, K comparable, T any](kf KF) func(F) F { + return CacheCallback[func(F) F, func() func() T](kf, getOrCreate[K, T]()) +} + +// getOrCreate is a naive implementation of a cache, without bounds +func getOrCreate[K comparable, T any]() func(K, func() func() T) func() T { + cache := make(map[K]func() T) + var l sync.Mutex + + return func(k K, cb func() func() T) func() T { + // only lock to access a lazy accessor to the value + l.Lock() + existing, ok := cache[k] + if !ok { + existing = cb() + cache[k] = existing + } + l.Unlock() + // compute the value outside of the lock + return existing + } +} + +// SingleElementCache is a cache with a capacity of a single element +func SingleElementCache[ + LLT ~func() LT, // generator of the generator + K comparable, // key into the cache + LT ~func() T, // generator of a value + T any, // the cached data type +]() func(K, LLT) LT { + var l sync.Mutex + + var key K + var value LT + hasKey := false + + return func(k K, gen LLT) LT { + l.Lock() + + existing := value + if !hasKey || key != k { + existing = gen() + // update state + key = k + value = existing + hasKey = true + } + + l.Unlock() + + return existing + } +} + +// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter +func CacheCallback[ + EM ~func(F) F, // endomorphism of the function + LLT ~func() LT, // generator of the generator + LT ~func() T, // generator of a value + F ~func(A) T, // function to actually cache + KF func(A) K, // extracts the cache key from the input + C ~func(K, LLT) LT, // the cache callback function + A any, K comparable, T any](kf KF, getOrCreate C) EM { + return func(f F) F { + return func(a A) T { + // cache entry + return getOrCreate(kf(a), func() LT { + return L.Memoize[LT](func() T { + return f(a) + }) + })() + } + } +} diff --git a/v2/function/generic/switch.go b/v2/function/generic/switch.go new file mode 100644 index 0000000..ea2bbbb --- /dev/null +++ b/v2/function/generic/switch.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +// Switch applies a handler to different cases. The handers are stored in a map. A key function +// extracts the case from a value. +func Switch[HF ~func(T) R, N ~map[K]HF, KF ~func(T) K, K comparable, T, R any](kf KF, n N, d HF) HF { + return func(t T) R { + f, ok := n[kf(t)] + if ok { + return f(t) + } + return d(t) + } +} diff --git a/v2/function/pipe_test.go b/v2/function/pipe_test.go new file mode 100644 index 0000000..eeb2d5d --- /dev/null +++ b/v2/function/pipe_test.go @@ -0,0 +1,62 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +import ( + "fmt" +) + +func addSthg(value int) int { + return value + 1 +} + +func doSthgElse(value int) int { + return value * 2 +} + +func doFinalSthg(value int) string { + return fmt.Sprintf("final value: %d", value) +} + +func Example() { + // start point + value := 1 + // imperative style + value1 := addSthg(value) // 2 + value2 := doSthgElse(value1) // 4 + finalValueImperative := doFinalSthg(value2) // "final value: 4" + + // the same but inline + finalValueInline := doFinalSthg(doSthgElse(addSthg(value))) + + // with pipe + finalValuePipe := Pipe3(value, addSthg, doSthgElse, doFinalSthg) + + // with flow + transform := Flow3(addSthg, doSthgElse, doFinalSthg) + finalValueFlow := transform(value) + + fmt.Println(finalValueImperative) + fmt.Println(finalValueInline) + fmt.Println(finalValuePipe) + fmt.Println(finalValueFlow) + + // Output: + // final value: 4 + // final value: 4 + // final value: 4 + // final value: 4 +} diff --git a/v2/function/ref.go b/v2/function/ref.go new file mode 100644 index 0000000..c40a269 --- /dev/null +++ b/v2/function/ref.go @@ -0,0 +1,24 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +func Ref[A any](a A) *A { + return &a +} + +func Deref[A any](a *A) A { + return *a +} diff --git a/v2/function/switch.go b/v2/function/switch.go new file mode 100644 index 0000000..ff18fce --- /dev/null +++ b/v2/function/switch.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +import ( + G "github.com/IBM/fp-go/v2/function/generic" +) + +// Switch applies a handler to different cases. The handers are stored in a map. A key function +// extracts the case from a value. +func Switch[K comparable, T, R any](kf func(T) K, n map[K]func(T) R, d func(T) R) func(T) R { + return G.Switch(kf, n, d) +} diff --git a/v2/function/ternary.go b/v2/function/ternary.go new file mode 100644 index 0000000..63d1959 --- /dev/null +++ b/v2/function/ternary.go @@ -0,0 +1,25 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +func Ternary[A, B any](pred func(A) bool, onTrue func(A) B, onFalse func(A) B) func(A) B { + return func(a A) B { + if pred(a) { + return onTrue(a) + } + return onFalse(a) + } +} diff --git a/v2/function/type.go b/v2/function/type.go new file mode 100644 index 0000000..ceee93a --- /dev/null +++ b/v2/function/type.go @@ -0,0 +1,20 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +func ToAny[A any](a A) any { + return any(a) +} diff --git a/v2/function/unvariadic_test.go b/v2/function/unvariadic_test.go new file mode 100644 index 0000000..e9f0bc7 --- /dev/null +++ b/v2/function/unvariadic_test.go @@ -0,0 +1,45 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package function + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func fromLibrary(data ...string) string { + return strings.Join(data, "-") +} + +func TestUnvariadic(t *testing.T) { + + res := Pipe1( + []string{"A", "B"}, + Unvariadic0(fromLibrary), + ) + + assert.Equal(t, "A-B", res) +} + +func TestVariadicArity(t *testing.T) { + + f := Unsliced2(Unvariadic0(fromLibrary)) + + res := f("A", "B") + assert.Equal(t, "A-B", res) +} diff --git a/v2/go.mod b/v2/go.mod new file mode 100644 index 0000000..755161b --- /dev/null +++ b/v2/go.mod @@ -0,0 +1,17 @@ +module github.com/IBM/fp-go/v2 + +go 1.24 + +require ( + github.com/stretchr/testify v1.10.0 + github.com/urfave/cli/v2 v2.27.5 +) + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/v2/go.sum b/v2/go.sum new file mode 100644 index 0000000..9767476 --- /dev/null +++ b/v2/go.sum @@ -0,0 +1,18 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/http/builder/builder.go b/v2/http/builder/builder.go new file mode 100644 index 0000000..fb13f0f --- /dev/null +++ b/v2/http/builder/builder.go @@ -0,0 +1,387 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package builder + +import ( + "bytes" + "crypto/sha256" + "fmt" + "net/http" + "net/url" + + A "github.com/IBM/fp-go/v2/array" + B "github.com/IBM/fp-go/v2/bytes" + E "github.com/IBM/fp-go/v2/either" + ENDO "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/http/content" + FM "github.com/IBM/fp-go/v2/http/form" + H "github.com/IBM/fp-go/v2/http/headers" + J "github.com/IBM/fp-go/v2/json" + LZ "github.com/IBM/fp-go/v2/lazy" + L "github.com/IBM/fp-go/v2/optics/lens" + O "github.com/IBM/fp-go/v2/option" + R "github.com/IBM/fp-go/v2/record" + S "github.com/IBM/fp-go/v2/string" + T "github.com/IBM/fp-go/v2/tuple" +) + +type ( + Builder struct { + method O.Option[string] + url string + headers http.Header + body O.Option[E.Either[error, []byte]] + query url.Values + } + + // Endomorphism returns an [ENDO.Endomorphism] that transforms a builder + Endomorphism = ENDO.Endomorphism[*Builder] +) + +var ( + // Default is the default builder + Default = &Builder{method: O.Some(defaultMethod()), headers: make(http.Header), body: noBody} + + defaultMethod = F.Constant(http.MethodGet) + + // Monoid is the [M.Monoid] for the [Endomorphism] + Monoid = ENDO.Monoid[*Builder]() + + // Url is a [L.Lens] for the URL + // + // Deprecated: use [URL] instead + Url = L.MakeLensRef((*Builder).GetURL, (*Builder).SetURL) + // URL is a [L.Lens] for the URL + URL = L.MakeLensRef((*Builder).GetURL, (*Builder).SetURL) + // Method is a [L.Lens] for the HTTP method + Method = L.MakeLensRef((*Builder).GetMethod, (*Builder).SetMethod) + // Body is a [L.Lens] for the request body + Body = L.MakeLensRef((*Builder).GetBody, (*Builder).SetBody) + // Headers is a [L.Lens] for the complete set of request headers + Headers = L.MakeLensRef((*Builder).GetHeaders, (*Builder).SetHeaders) + // Query is a [L.Lens] for the set of query parameters + Query = L.MakeLensRef((*Builder).GetQuery, (*Builder).SetQuery) + + rawQuery = L.MakeLensRef(getRawQuery, setRawQuery) + + getHeader = F.Bind2of2((*Builder).GetHeader) + delHeader = F.Bind2of2((*Builder).DelHeader) + setHeader = F.Bind2of3((*Builder).SetHeader) + + noHeader = O.None[string]() + noBody = O.None[E.Either[error, []byte]]() + noQueryArg = O.None[string]() + + parseURL = E.Eitherize1(url.Parse) + parseQuery = E.Eitherize1(url.ParseQuery) + + // WithQuery creates a [Endomorphism] for a complete set of query parameters + WithQuery = Query.Set + // WithMethod creates a [Endomorphism] for a certain method + WithMethod = Method.Set + // WithUrl creates a [Endomorphism] for the URL + // + // Deprecated: use [WithURL] instead + WithUrl = URL.Set + // WithURL creates a [Endomorphism] for the URL + WithURL = URL.Set + // WithHeaders creates a [Endomorphism] for a set of headers + WithHeaders = Headers.Set + // WithBody creates a [Endomorphism] for a request body + WithBody = F.Flow2( + O.Of[E.Either[error, []byte]], + Body.Set, + ) + // WithBytes creates a [Endomorphism] for a request body using bytes + WithBytes = F.Flow2( + E.Of[error, []byte], + WithBody, + ) + // WithContentType adds the [H.ContentType] header + WithContentType = WithHeader(H.ContentType) + // WithAuthorization adds the [H.Authorization] header + WithAuthorization = WithHeader(H.Authorization) + + // WithGet adds the [http.MethodGet] method + WithGet = WithMethod(http.MethodGet) + // WithPost adds the [http.MethodPost] method + WithPost = WithMethod(http.MethodPost) + // WithPut adds the [http.MethodPut] method + WithPut = WithMethod(http.MethodPut) + // WithDelete adds the [http.MethodDelete] method + WithDelete = WithMethod(http.MethodDelete) + + // WithBearer creates a [Endomorphism] to add a Bearer [H.Authorization] header + WithBearer = F.Flow2( + S.Format[string]("Bearer %s"), + WithAuthorization, + ) + + // WithoutBody creates a [Endomorphism] to remove the body + WithoutBody = F.Pipe1( + noBody, + Body.Set, + ) + + // WithFormData creates a [Endomorphism] to send form data payload + WithFormData = F.Flow4( + url.Values.Encode, + S.ToBytes, + WithBytes, + ENDO.Chain(WithContentType(C.FormEncoded)), + ) + + // bodyAsBytes returns a []byte with a fallback to the empty array + bodyAsBytes = O.Fold(B.Empty, E.Fold(F.Ignore1of1[error](B.Empty), F.Identity[[]byte])) +) + +func setRawQuery(u *url.URL, raw string) *url.URL { + u.RawQuery = raw + return u +} + +func getRawQuery(u *url.URL) string { + return u.RawQuery +} + +func (builder *Builder) clone() *Builder { + cpy := *builder + cpy.headers = cpy.headers.Clone() + return &cpy +} + +// GetTargetUrl constructs a full URL with query parameters on top of the provided URL string +// +// Deprecated: use [GetTargetURL] instead +func (builder *Builder) GetTargetUrl() E.Either[error, string] { + return builder.GetTargetURL() +} + +// GetTargetURL constructs a full URL with query parameters on top of the provided URL string +func (builder *Builder) GetTargetURL() E.Either[error, string] { + // construct the final URL + return F.Pipe3( + builder, + Url.Get, + parseURL, + E.Chain(F.Flow4( + T.Replicate2[*url.URL], + T.Map2( + F.Flow2( + F.Curry2(setRawQuery), + E.Of[error, func(string) *url.URL], + ), + F.Flow3( + rawQuery.Get, + parseQuery, + E.Map[error](F.Flow2( + F.Curry2(FM.ValuesMonoid.Concat)(builder.GetQuery()), + (url.Values).Encode, + )), + ), + ), + T.Tupled2(E.MonadAp[*url.URL, error, string]), + E.Map[error]((*url.URL).String), + )), + ) +} + +// Deprecated: use [GetURL] instead +func (builder *Builder) GetUrl() string { + return builder.url +} + +func (builder *Builder) GetURL() string { + return builder.url +} + +func (builder *Builder) GetMethod() string { + return F.Pipe1( + builder.method, + O.GetOrElse(defaultMethod), + ) +} + +func (builder *Builder) GetHeaders() http.Header { + return builder.headers +} + +func (builder *Builder) GetQuery() url.Values { + return builder.query +} + +func (builder *Builder) SetQuery(query url.Values) *Builder { + builder.query = query + return builder +} + +func (builder *Builder) GetBody() O.Option[E.Either[error, []byte]] { + return builder.body +} + +func (builder *Builder) SetMethod(method string) *Builder { + builder.method = O.Some(method) + return builder +} + +// Deprecated: use [SetURL] instead +func (builder *Builder) SetUrl(url string) *Builder { + builder.url = url + return builder +} + +func (builder *Builder) SetURL(url string) *Builder { + builder.url = url + return builder +} + +func (builder *Builder) SetHeaders(headers http.Header) *Builder { + builder.headers = headers + return builder +} + +func (builder *Builder) SetBody(body O.Option[E.Either[error, []byte]]) *Builder { + builder.body = body + return builder +} + +func (builder *Builder) SetHeader(name, value string) *Builder { + builder.headers.Set(name, value) + return builder +} + +func (builder *Builder) DelHeader(name string) *Builder { + builder.headers.Del(name) + return builder +} + +func (builder *Builder) GetHeader(name string) O.Option[string] { + return F.Pipe2( + name, + builder.headers.Get, + O.FromPredicate(S.IsNonEmpty), + ) +} + +func (builder *Builder) GetHeaderValues(name string) []string { + return builder.headers.Values(name) +} + +// GetHash returns a hash value for the builder that can be used as a cache key +func (builder *Builder) GetHash() string { + return MakeHash(builder) +} + +// Header returns a [L.Lens] for a single header +func Header(name string) L.Lens[*Builder, O.Option[string]] { + get := getHeader(name) + set := F.Bind1of2(setHeader(name)) + del := F.Flow2( + LZ.Of[*Builder], + LZ.Map(delHeader(name)), + ) + + return L.MakeLens(get, func(b *Builder, value O.Option[string]) *Builder { + cpy := b.clone() + return F.Pipe1( + value, + O.Fold(del(cpy), set(cpy)), + ) + }) +} + +// WithHeader creates a [Endomorphism] for a certain header +func WithHeader(name string) func(value string) Endomorphism { + return F.Flow2( + O.Of[string], + Header(name).Set, + ) +} + +// WithoutHeader creates a [Endomorphism] to remove a certain header +func WithoutHeader(name string) Endomorphism { + return Header(name).Set(noHeader) +} + +// WithJson creates a [Endomorphism] to send JSON payload +// +// Deprecated: use [WithJSON] instead +func WithJson[T any](data T) Endomorphism { + return WithJSON[T](data) +} + +// WithJSON creates a [Endomorphism] to send JSON payload +func WithJSON[T any](data T) Endomorphism { + return Monoid.Concat( + F.Pipe2( + data, + J.Marshal[T], + WithBody, + ), + WithContentType(C.JSON), + ) +} + +// QueryArg is a [L.Lens] for the first value of a query argument +func QueryArg(name string) L.Lens[*Builder, O.Option[string]] { + return F.Pipe1( + Query, + L.Compose[*Builder](FM.AtValue(name)), + ) +} + +// WithQueryArg creates a [Endomorphism] for a certain query argument +func WithQueryArg(name string) func(value string) Endomorphism { + return F.Flow2( + O.Of[string], + QueryArg(name).Set, + ) +} + +// WithoutQueryArg creates a [Endomorphism] that removes a query argument +func WithoutQueryArg(name string) Endomorphism { + return QueryArg(name).Set(noQueryArg) +} + +func hashWriteValue(buf *bytes.Buffer, value string) *bytes.Buffer { + buf.WriteString(value) + return buf +} + +func hashWriteQuery(name string, buf *bytes.Buffer, values []string) *bytes.Buffer { + buf.WriteString(name) + return A.Reduce(hashWriteValue, buf)(values) +} + +func makeBytes(b *Builder) []byte { + var buf bytes.Buffer + + buf.WriteString(b.GetMethod()) + buf.WriteString(b.GetURL()) + b.GetHeaders().Write(&buf) // #nosec: G104 + + R.ReduceOrdWithIndex[[]string, *bytes.Buffer](S.Ord)(hashWriteQuery, &buf)(b.GetQuery()) + + buf.Write(bodyAsBytes(b.GetBody())) + + return buf.Bytes() +} + +// MakeHash converts a [Builder] into a hash string, convenient to use as a cache key +func MakeHash(b *Builder) string { + return fmt.Sprintf("%x", sha256.Sum256(makeBytes(b))) +} diff --git a/v2/http/builder/builder_test.go b/v2/http/builder/builder_test.go new file mode 100644 index 0000000..f94ec33 --- /dev/null +++ b/v2/http/builder/builder_test.go @@ -0,0 +1,93 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package builder + +import ( + "fmt" + "testing" + + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/http/content" + FD "github.com/IBM/fp-go/v2/http/form" + H "github.com/IBM/fp-go/v2/http/headers" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +func TestBuilder(t *testing.T) { + + name := H.ContentType + withContentType := WithHeader(name) + withoutContentType := WithoutHeader(name) + + b1 := F.Pipe1( + Default, + withContentType(C.JSON), + ) + + b2 := F.Pipe1( + b1, + withContentType(C.TextPlain), + ) + + b3 := F.Pipe1( + b2, + withoutContentType, + ) + + assert.Equal(t, O.None[string](), Default.GetHeader(name)) + assert.Equal(t, O.Of(C.JSON), b1.GetHeader(name)) + assert.Equal(t, O.Of(C.TextPlain), b2.GetHeader(name)) + assert.Equal(t, O.None[string](), b3.GetHeader(name)) +} + +func TestWithFormData(t *testing.T) { + data := F.Pipe1( + FD.Default, + FD.WithValue("a")("b"), + ) + + res := F.Pipe1( + Default, + WithFormData(data), + ) + + assert.Equal(t, C.FormEncoded, Headers.Get(res).Get(H.ContentType)) +} + +func TestHash(t *testing.T) { + + b1 := F.Pipe4( + Default, + WithContentType(C.JSON), + WithHeader(H.Accept)(C.JSON), + WithURL("http://www.example.com"), + WithJSON(map[string]string{"a": "b"}), + ) + + b2 := F.Pipe4( + Default, + WithURL("http://www.example.com"), + WithHeader(H.Accept)(C.JSON), + WithContentType(C.JSON), + WithJSON(map[string]string{"a": "b"}), + ) + + assert.Equal(t, MakeHash(b1), MakeHash(b2)) + assert.NotEqual(t, MakeHash(Default), MakeHash(b2)) + + fmt.Println(MakeHash(b1)) +} diff --git a/v2/http/content/content.go b/v2/http/content/content.go new file mode 100644 index 0000000..30f21bb --- /dev/null +++ b/v2/http/content/content.go @@ -0,0 +1,23 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package content + +const ( + TextPlain = "text/plain" + JSON = "application/json" + Json = JSON // Deprecated: use [JSON] instead + FormEncoded = "application/x-www-form-urlencoded" +) diff --git a/v2/http/form/form.go b/v2/http/form/form.go new file mode 100644 index 0000000..942d10d --- /dev/null +++ b/v2/http/form/form.go @@ -0,0 +1,74 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package form + +import ( + "net/url" + + A "github.com/IBM/fp-go/v2/array" + ENDO "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/optics/lens" + LA "github.com/IBM/fp-go/v2/optics/lens/array" + LRG "github.com/IBM/fp-go/v2/optics/lens/record/generic" + O "github.com/IBM/fp-go/v2/option" + RG "github.com/IBM/fp-go/v2/record/generic" +) + +type ( + // Endomorphism returns an [ENDO.Endomorphism] that transforms a form + Endomorphism = ENDO.Endomorphism[url.Values] +) + +var ( + // Default is the default form field + Default = make(url.Values) + + noField = O.None[string]() + + // Monoid is the [M.Monoid] for the [Endomorphism] + Monoid = ENDO.Monoid[url.Values]() + + // ValuesMonoid is a [M.Monoid] to concatenate [url.Values] maps + ValuesMonoid = RG.UnionMonoid[url.Values](A.Semigroup[string]()) + + // AtValues is a [L.Lens] that focusses on the values of a form field + AtValues = LRG.AtRecord[url.Values, []string] + + composeHead = F.Pipe1( + LA.AtHead[string](), + L.ComposeOptions[url.Values, string](A.Empty[string]()), + ) + + // AtValue is a [L.Lens] that focusses on first value in form fields + AtValue = F.Flow2( + AtValues, + composeHead, + ) +) + +// WithValue creates a [FormBuilder] for a certain field +func WithValue(name string) func(value string) Endomorphism { + return F.Flow2( + O.Of[string], + AtValue(name).Set, + ) +} + +// WithoutValue creates a [FormBuilder] that removes a field +func WithoutValue(name string) Endomorphism { + return AtValue(name).Set(noField) +} diff --git a/v2/http/form/form_test.go b/v2/http/form/form_test.go new file mode 100644 index 0000000..9eba9af --- /dev/null +++ b/v2/http/form/form_test.go @@ -0,0 +1,93 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package form + +import ( + "net/url" + "testing" + + A "github.com/IBM/fp-go/v2/array" + "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" + LT "github.com/IBM/fp-go/v2/optics/lens/testing" + O "github.com/IBM/fp-go/v2/option" + RG "github.com/IBM/fp-go/v2/record/generic" + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +var ( + sEq = eq.FromEquals(S.Eq) + valuesEq = RG.Eq[url.Values](A.Eq(sEq)) +) + +func TestLaws(t *testing.T) { + name := "Content-Type" + fieldLaws := LT.AssertLaws[url.Values, O.Option[string]](t, O.Eq(sEq), valuesEq)(AtValue(name)) + + n := O.None[string]() + s1 := O.Some("s1") + + v1 := F.Pipe1( + Default, + WithValue(name)("v1"), + ) + + v2 := F.Pipe1( + Default, + WithValue("Other-Header")("v2"), + ) + + assert.True(t, fieldLaws(Default, n)) + assert.True(t, fieldLaws(v1, n)) + assert.True(t, fieldLaws(v2, n)) + + assert.True(t, fieldLaws(Default, s1)) + assert.True(t, fieldLaws(v1, s1)) + assert.True(t, fieldLaws(v2, s1)) +} + +func TestFormField(t *testing.T) { + + v1 := F.Pipe1( + Default, + WithValue("h1")("v1"), + ) + + v2 := F.Pipe1( + v1, + WithValue("h2")("v2"), + ) + + // make sure the code does not change structures + assert.False(t, valuesEq.Equals(Default, v1)) + assert.False(t, valuesEq.Equals(Default, v2)) + assert.False(t, valuesEq.Equals(v1, v2)) + + // check for existence of values + assert.Equal(t, "v1", v1.Get("h1")) + assert.Equal(t, "v1", v2.Get("h1")) + assert.Equal(t, "v2", v2.Get("h2")) + + // check getter on lens + + l1 := AtValue("h1") + l2 := AtValue("h2") + + assert.Equal(t, O.Of("v1"), l1.Get(v1)) + assert.Equal(t, O.Of("v1"), l1.Get(v2)) + assert.Equal(t, O.Of("v2"), l2.Get(v2)) +} diff --git a/v2/http/headers/headers.go b/v2/http/headers/headers.go new file mode 100644 index 0000000..693db45 --- /dev/null +++ b/v2/http/headers/headers.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package headers + +import ( + "net/http" + "net/textproto" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/optics/lens" + LA "github.com/IBM/fp-go/v2/optics/lens/array" + LRG "github.com/IBM/fp-go/v2/optics/lens/record/generic" + RG "github.com/IBM/fp-go/v2/record/generic" +) + +// HTTP headers +const ( + Accept = "Accept" + Authorization = "Authorization" + ContentType = "Content-Type" + ContentLength = "Content-Length" +) + +var ( + // Monoid is a [M.Monoid] to concatenate [http.Header] maps + Monoid = RG.UnionMonoid[http.Header](A.Semigroup[string]()) + + // AtValues is a [L.Lens] that focusses on the values of a header + AtValues = F.Flow2( + textproto.CanonicalMIMEHeaderKey, + LRG.AtRecord[http.Header, []string], + ) + + composeHead = F.Pipe1( + LA.AtHead[string](), + L.ComposeOptions[http.Header, string](A.Empty[string]()), + ) + + // AtValue is a [L.Lens] that focusses on first value of a header + AtValue = F.Flow2( + AtValues, + composeHead, + ) +) diff --git a/v2/http/headers/headers_test.go b/v2/http/headers/headers_test.go new file mode 100644 index 0000000..830673a --- /dev/null +++ b/v2/http/headers/headers_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package headers + +import ( + "net/http" + "testing" + + A "github.com/IBM/fp-go/v2/array" + "github.com/IBM/fp-go/v2/eq" + LT "github.com/IBM/fp-go/v2/optics/lens/testing" + O "github.com/IBM/fp-go/v2/option" + RG "github.com/IBM/fp-go/v2/record/generic" + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +var ( + sEq = eq.FromEquals(S.Eq) + valuesEq = RG.Eq[http.Header](A.Eq(sEq)) +) + +func TestLaws(t *testing.T) { + name := "Content-Type" + fieldLaws := LT.AssertLaws[http.Header, O.Option[string]](t, O.Eq(sEq), valuesEq)(AtValue(name)) + + n := O.None[string]() + s1 := O.Some("s1") + + def := make(http.Header) + + v1 := make(http.Header) + v1.Set(name, "v1") + + v2 := make(http.Header) + v2.Set("Other-Header", "v2") + + assert.True(t, fieldLaws(def, n)) + assert.True(t, fieldLaws(v1, n)) + assert.True(t, fieldLaws(v2, n)) + + assert.True(t, fieldLaws(def, s1)) + assert.True(t, fieldLaws(v1, s1)) + assert.True(t, fieldLaws(v2, s1)) +} diff --git a/v2/http/types.go b/v2/http/types.go new file mode 100644 index 0000000..f0d758c --- /dev/null +++ b/v2/http/types.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + H "net/http" + + P "github.com/IBM/fp-go/v2/pair" +) + +type ( + // FullResponse represents a full http response, including headers and body + FullResponse = P.Pair[*H.Response, []byte] +) + +var ( + Response = P.Head[*H.Response, []byte] + Body = P.Tail[*H.Response, []byte] +) diff --git a/v2/http/utils.go b/v2/http/utils.go new file mode 100644 index 0000000..b0c510d --- /dev/null +++ b/v2/http/utils.go @@ -0,0 +1,131 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "fmt" + "io" + "mime" + H "net/http" + "net/url" + "regexp" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" + R "github.com/IBM/fp-go/v2/record/generic" +) + +type ( + ParsedMediaType = P.Pair[string, map[string]string] + + HttpError struct { + statusCode int + headers H.Header + body []byte + url *url.URL + } +) + +var ( + // mime type to check if a media type matches + isJSONMimeType = regexp.MustCompile(`application/(?:\w+\+)?json`).MatchString + // ValidateResponse validates an HTTP response and returns an [E.Either] if the response is not a success + ValidateResponse = E.FromPredicate(isValidStatus, StatusCodeError) + // alidateJsonContentTypeString parses a content type a validates that it is valid JSON + validateJSONContentTypeString = F.Flow2( + ParseMediaType, + E.ChainFirst(F.Flow2( + P.Head[string, map[string]string], + E.FromPredicate(isJSONMimeType, errors.OnSome[string]("mimetype [%s] is not a valid JSON content type")), + )), + ) + // ValidateJSONResponse checks if an HTTP response is a valid JSON response + ValidateJSONResponse = F.Flow2( + E.Of[error, *H.Response], + E.ChainFirst(F.Flow5( + GetHeader, + R.Lookup[H.Header](HeaderContentType), + O.Chain(A.First[string]), + E.FromOption[string](errors.OnNone("unable to access the [%s] header", HeaderContentType)), + E.ChainFirst(validateJSONContentTypeString), + ))) + // ValidateJsonResponse checks if an HTTP response is a valid JSON response + // + // Deprecated: use [ValidateJSONResponse] instead + ValidateJsonResponse = ValidateJSONResponse +) + +const ( + HeaderContentType = "Content-Type" +) + +// ParseMediaType parses a media type into a tuple +func ParseMediaType(mediaType string) E.Either[error, ParsedMediaType] { + m, p, err := mime.ParseMediaType(mediaType) + return E.TryCatchError(P.MakePair(m, p), err) +} + +// Error fulfills the error interface +func (r *HttpError) Error() string { + return fmt.Sprintf("invalid status code [%d] when accessing URL [%s]", r.statusCode, r.url) +} + +func (r *HttpError) String() string { + return r.Error() +} + +func (r *HttpError) StatusCode() int { + return r.statusCode +} + +func (r *HttpError) Headers() H.Header { + return r.headers +} + +func (r *HttpError) URL() *url.URL { + return r.url +} + +func (r *HttpError) Body() []byte { + return r.body +} + +func GetHeader(resp *H.Response) H.Header { + return resp.Header +} + +func GetBody(resp *H.Response) io.ReadCloser { + return resp.Body +} + +func isValidStatus(resp *H.Response) bool { + return resp.StatusCode >= H.StatusOK && resp.StatusCode < H.StatusMultipleChoices +} + +// StatusCodeError creates an instance of [HttpError] filled with information from the response +func StatusCodeError(resp *H.Response) error { + // read the body + bodyRdr := GetBody(resp) + defer bodyRdr.Close() + // try to access body content + body, _ := io.ReadAll(bodyRdr) + // return an error with comprehensive information + return &HttpError{statusCode: resp.StatusCode, headers: GetHeader(resp).Clone(), body: body, url: resp.Request.URL} +} diff --git a/v2/http/utils_test.go b/v2/http/utils_test.go new file mode 100644 index 0000000..51de24f --- /dev/null +++ b/v2/http/utils_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/http/content" + "github.com/stretchr/testify/assert" +) + +func NoError[A any](t *testing.T) func(E.Either[error, A]) bool { + return E.Fold(func(err error) bool { + return assert.NoError(t, err) + }, F.Constant1[A](true)) +} + +func Error[A any](t *testing.T) func(E.Either[error, A]) bool { + return E.Fold(F.Constant1[error](true), func(A) bool { + return assert.Error(t, nil) + }) +} + +func TestValidateJsonContentTypeString(t *testing.T) { + + res := F.Pipe1( + validateJSONContentTypeString(C.JSON), + NoError[ParsedMediaType](t), + ) + + assert.True(t, res) +} + +func TestValidateInvalidJsonContentTypeString(t *testing.T) { + + res := F.Pipe1( + validateJSONContentTypeString("application/xml"), + Error[ParsedMediaType](t), + ) + + assert.True(t, res) +} diff --git a/v2/identity/bind.go b/v2/identity/bind.go new file mode 100644 index 0000000..868ae72 --- /dev/null +++ b/v2/identity/bind.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package identity + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) S { + return empty +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(S1) S2 { + return C.Bind( + Chain[S1, S2], + Map[T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(S1) S2 { + return F.Let( + Map[S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(S1) S2 { + return F.LetTo( + Map[S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func(T) S1 { + return C.BindTo( + Map[T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa T, +) func(S1) S2 { + return A.ApS( + Ap[S2, T], + Map[S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/identity/doc.go b/v2/identity/doc.go new file mode 100644 index 0000000..237ddaa --- /dev/null +++ b/v2/identity/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package identity + +//go:generate go run .. identity --count 10 --filename gen.go diff --git a/v2/identity/gen.go b/v2/identity/gen.go new file mode 100644 index 0000000..26b0b36 --- /dev/null +++ b/v2/identity/gen.go @@ -0,0 +1,505 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:02.7798504 +0100 CET m=+0.021933401 + +package identity + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT1 converts 1 parameters of [T] into a [Tuple1]. +func SequenceT1[T1 any](t1 T1) T.Tuple1[T1] { + return A.SequenceT1( + Map[T1, T.Tuple1[T1]], + t1, + ) +} + +// SequenceTuple1 converts a [Tuple1] of [T] into an [Tuple1]. +func SequenceTuple1[T1 any](t T.Tuple1[T1]) T.Tuple1[T1] { + return A.SequenceTuple1( + Map[T1, T.Tuple1[T1]], + t, + ) +} + +// TraverseTuple1 converts a [Tuple1] of [A] via transformation functions transforming [A] to [A] into a [Tuple1]. +func TraverseTuple1[F1 ~func(A1) T1, A1, T1 any](f1 F1) func(T.Tuple1[A1]) T.Tuple1[T1] { + return func(t T.Tuple1[A1]) T.Tuple1[T1] { + return A.TraverseTuple1( + Map[T1, T.Tuple1[T1]], + f1, + t, + ) + } +} + +// SequenceT2 converts 2 parameters of [T] into a [Tuple2]. +func SequenceT2[T1, T2 any](t1 T1, t2 T2) T.Tuple2[T1, T2] { + return A.SequenceT2( + Map[T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], T2], + t1, + t2, + ) +} + +// SequenceTuple2 converts a [Tuple2] of [T] into an [Tuple2]. +func SequenceTuple2[T1, T2 any](t T.Tuple2[T1, T2]) T.Tuple2[T1, T2] { + return A.SequenceTuple2( + Map[T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], T2], + t, + ) +} + +// TraverseTuple2 converts a [Tuple2] of [A] via transformation functions transforming [A] to [A] into a [Tuple2]. +func TraverseTuple2[F1 ~func(A1) T1, F2 ~func(A2) T2, A1, T1, A2, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) T.Tuple2[T1, T2] { + return func(t T.Tuple2[A1, A2]) T.Tuple2[T1, T2] { + return A.TraverseTuple2( + Map[T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], T2], + f1, + f2, + t, + ) + } +} + +// SequenceT3 converts 3 parameters of [T] into a [Tuple3]. +func SequenceT3[T1, T2, T3 any](t1 T1, t2 T2, t3 T3) T.Tuple3[T1, T2, T3] { + return A.SequenceT3( + Map[T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], T2], + Ap[T.Tuple3[T1, T2, T3], T3], + t1, + t2, + t3, + ) +} + +// SequenceTuple3 converts a [Tuple3] of [T] into an [Tuple3]. +func SequenceTuple3[T1, T2, T3 any](t T.Tuple3[T1, T2, T3]) T.Tuple3[T1, T2, T3] { + return A.SequenceTuple3( + Map[T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], T2], + Ap[T.Tuple3[T1, T2, T3], T3], + t, + ) +} + +// TraverseTuple3 converts a [Tuple3] of [A] via transformation functions transforming [A] to [A] into a [Tuple3]. +func TraverseTuple3[F1 ~func(A1) T1, F2 ~func(A2) T2, F3 ~func(A3) T3, A1, T1, A2, T2, A3, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) T.Tuple3[T1, T2, T3] { + return func(t T.Tuple3[A1, A2, A3]) T.Tuple3[T1, T2, T3] { + return A.TraverseTuple3( + Map[T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], T2], + Ap[T.Tuple3[T1, T2, T3], T3], + f1, + f2, + f3, + t, + ) + } +} + +// SequenceT4 converts 4 parameters of [T] into a [Tuple4]. +func SequenceT4[T1, T2, T3, T4 any](t1 T1, t2 T2, t3 T3, t4 T4) T.Tuple4[T1, T2, T3, T4] { + return A.SequenceT4( + Map[T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], T3], + Ap[T.Tuple4[T1, T2, T3, T4], T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceTuple4 converts a [Tuple4] of [T] into an [Tuple4]. +func SequenceTuple4[T1, T2, T3, T4 any](t T.Tuple4[T1, T2, T3, T4]) T.Tuple4[T1, T2, T3, T4] { + return A.SequenceTuple4( + Map[T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], T3], + Ap[T.Tuple4[T1, T2, T3, T4], T4], + t, + ) +} + +// TraverseTuple4 converts a [Tuple4] of [A] via transformation functions transforming [A] to [A] into a [Tuple4]. +func TraverseTuple4[F1 ~func(A1) T1, F2 ~func(A2) T2, F3 ~func(A3) T3, F4 ~func(A4) T4, A1, T1, A2, T2, A3, T3, A4, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) T.Tuple4[T1, T2, T3, T4] { + return func(t T.Tuple4[A1, A2, A3, A4]) T.Tuple4[T1, T2, T3, T4] { + return A.TraverseTuple4( + Map[T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], T3], + Ap[T.Tuple4[T1, T2, T3, T4], T4], + f1, + f2, + f3, + f4, + t, + ) + } +} + +// SequenceT5 converts 5 parameters of [T] into a [Tuple5]. +func SequenceT5[T1, T2, T3, T4, T5 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) T.Tuple5[T1, T2, T3, T4, T5] { + return A.SequenceT5( + Map[T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceTuple5 converts a [Tuple5] of [T] into an [Tuple5]. +func SequenceTuple5[T1, T2, T3, T4, T5 any](t T.Tuple5[T1, T2, T3, T4, T5]) T.Tuple5[T1, T2, T3, T4, T5] { + return A.SequenceTuple5( + Map[T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], T5], + t, + ) +} + +// TraverseTuple5 converts a [Tuple5] of [A] via transformation functions transforming [A] to [A] into a [Tuple5]. +func TraverseTuple5[F1 ~func(A1) T1, F2 ~func(A2) T2, F3 ~func(A3) T3, F4 ~func(A4) T4, F5 ~func(A5) T5, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) T.Tuple5[T1, T2, T3, T4, T5] { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) T.Tuple5[T1, T2, T3, T4, T5] { + return A.TraverseTuple5( + Map[T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], T5], + f1, + f2, + f3, + f4, + f5, + t, + ) + } +} + +// SequenceT6 converts 6 parameters of [T] into a [Tuple6]. +func SequenceT6[T1, T2, T3, T4, T5, T6 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) T.Tuple6[T1, T2, T3, T4, T5, T6] { + return A.SequenceT6( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceTuple6 converts a [Tuple6] of [T] into an [Tuple6]. +func SequenceTuple6[T1, T2, T3, T4, T5, T6 any](t T.Tuple6[T1, T2, T3, T4, T5, T6]) T.Tuple6[T1, T2, T3, T4, T5, T6] { + return A.SequenceTuple6( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], T6], + t, + ) +} + +// TraverseTuple6 converts a [Tuple6] of [A] via transformation functions transforming [A] to [A] into a [Tuple6]. +func TraverseTuple6[F1 ~func(A1) T1, F2 ~func(A2) T2, F3 ~func(A3) T3, F4 ~func(A4) T4, F5 ~func(A5) T5, F6 ~func(A6) T6, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) T.Tuple6[T1, T2, T3, T4, T5, T6] { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) T.Tuple6[T1, T2, T3, T4, T5, T6] { + return A.TraverseTuple6( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], T6], + f1, + f2, + f3, + f4, + f5, + f6, + t, + ) + } +} + +// SequenceT7 converts 7 parameters of [T] into a [Tuple7]. +func SequenceT7[T1, T2, T3, T4, T5, T6, T7 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return A.SequenceT7( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceTuple7 converts a [Tuple7] of [T] into an [Tuple7]. +func SequenceTuple7[T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[T1, T2, T3, T4, T5, T6, T7]) T.Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return A.SequenceTuple7( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T7], + t, + ) +} + +// TraverseTuple7 converts a [Tuple7] of [A] via transformation functions transforming [A] to [A] into a [Tuple7]. +func TraverseTuple7[F1 ~func(A1) T1, F2 ~func(A2) T2, F3 ~func(A3) T3, F4 ~func(A4) T4, F5 ~func(A5) T5, F6 ~func(A6) T6, F7 ~func(A7) T7, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) T.Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) T.Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return A.TraverseTuple7( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t, + ) + } +} + +// SequenceT8 converts 8 parameters of [T] into a [Tuple8]. +func SequenceT8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return A.SequenceT8( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceTuple8 converts a [Tuple8] of [T] into an [Tuple8]. +func SequenceTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return A.SequenceTuple8( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T8], + t, + ) +} + +// TraverseTuple8 converts a [Tuple8] of [A] via transformation functions transforming [A] to [A] into a [Tuple8]. +func TraverseTuple8[F1 ~func(A1) T1, F2 ~func(A2) T2, F3 ~func(A3) T3, F4 ~func(A4) T4, F5 ~func(A5) T5, F6 ~func(A6) T6, F7 ~func(A7) T7, F8 ~func(A8) T8, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return A.TraverseTuple8( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t, + ) + } +} + +// SequenceT9 converts 9 parameters of [T] into a [Tuple9]. +func SequenceT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return A.SequenceT9( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceTuple9 converts a [Tuple9] of [T] into an [Tuple9]. +func SequenceTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return A.SequenceTuple9( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T9], + t, + ) +} + +// TraverseTuple9 converts a [Tuple9] of [A] via transformation functions transforming [A] to [A] into a [Tuple9]. +func TraverseTuple9[F1 ~func(A1) T1, F2 ~func(A2) T2, F3 ~func(A3) T3, F4 ~func(A4) T4, F5 ~func(A5) T5, F6 ~func(A6) T6, F7 ~func(A7) T7, F8 ~func(A8) T8, F9 ~func(A9) T9, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return A.TraverseTuple9( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t, + ) + } +} + +// SequenceT10 converts 10 parameters of [T] into a [Tuple10]. +func SequenceT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return A.SequenceT10( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceTuple10 converts a [Tuple10] of [T] into an [Tuple10]. +func SequenceTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return A.SequenceTuple10( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T10], + t, + ) +} + +// TraverseTuple10 converts a [Tuple10] of [A] via transformation functions transforming [A] to [A] into a [Tuple10]. +func TraverseTuple10[F1 ~func(A1) T1, F2 ~func(A2) T2, F3 ~func(A3) T3, F4 ~func(A4) T4, F5 ~func(A5) T5, F6 ~func(A6) T6, F7 ~func(A7) T7, F8 ~func(A8) T8, F9 ~func(A9) T9, F10 ~func(A10) T10, A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return A.TraverseTuple10( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t, + ) + } +} diff --git a/v2/identity/generic/identity.go b/v2/identity/generic/identity.go new file mode 100644 index 0000000..db0d858 --- /dev/null +++ b/v2/identity/generic/identity.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + FC "github.com/IBM/fp-go/v2/internal/functor" +) + +func MonadAp[GAB ~func(A) B, B, A any](fab GAB, fa A) B { + return fab(fa) +} + +func Ap[GAB ~func(A) B, B, A any](fa A) func(GAB) B { + return F.Bind2nd(MonadAp[GAB, B, A], fa) +} + +func MonadMap[GAB ~func(A) B, A, B any](fa A, f GAB) B { + return f(fa) +} + +func Map[GAB ~func(A) B, A, B any](f GAB) func(A) B { + return f +} + +func MonadChain[GAB ~func(A) B, A, B any](ma A, f GAB) B { + return f(ma) +} + +func Chain[GAB ~func(A) B, A, B any](f GAB) func(A) B { + return f +} + +func MonadChainFirst[GAB ~func(A) B, A, B any](fa A, f GAB) A { + return C.MonadChainFirst(MonadChain[func(A) A, A, A], MonadMap[func(B) A, B, A], fa, f) +} + +func ChainFirst[GAB ~func(A) B, A, B any](f GAB) func(A) A { + return C.ChainFirst( + Chain[func(A) A, A, A], + Map[func(B) A, B, A], + f, + ) +} + +func MonadFlap[GAB ~func(A) B, A, B any](fab GAB, a A) B { + return FC.MonadFlap(MonadMap[func(GAB) B, GAB, B], fab, a) +} + +func Flap[GAB ~func(A) B, B, A any](a A) func(GAB) B { + return FC.Flap(Map[func(GAB) B, GAB, B], a) +} diff --git a/v2/identity/identity.go b/v2/identity/identity.go new file mode 100644 index 0000000..3f7e8b6 --- /dev/null +++ b/v2/identity/identity.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package identity + +import ( + F "github.com/IBM/fp-go/v2/function" + G "github.com/IBM/fp-go/v2/identity/generic" +) + +func MonadAp[B, A any](fab func(A) B, fa A) B { + return G.MonadAp(fab, fa) +} + +func Ap[B, A any](fa A) func(func(A) B) B { + return G.Ap[func(A) B](fa) +} + +func MonadMap[A, B any](fa A, f func(A) B) B { + return G.MonadMap(fa, f) +} + +func Map[A, B any](f func(A) B) func(A) B { + return G.Map(f) +} + +func MonadMapTo[A, B any](_ A, b B) B { + return b +} + +func MapTo[A, B any](b B) func(A) B { + return F.Constant1[A](b) +} + +func Of[A any](a A) A { + return a +} + +func MonadChain[A, B any](ma A, f func(A) B) B { + return G.MonadChain(ma, f) +} + +func Chain[A, B any](f func(A) B) func(A) B { + return G.Chain(f) +} + +func MonadChainFirst[A, B any](fa A, f func(A) B) A { + return G.MonadChainFirst(fa, f) +} + +func ChainFirst[A, B any](f func(A) B) func(A) A { + return G.ChainFirst(f) +} + +func MonadFlap[B, A any](fab func(A) B, a A) B { + return G.MonadFlap[func(A) B](fab, a) +} + +func Flap[B, A any](a A) func(func(A) B) B { + return G.Flap[func(A) B](a) +} diff --git a/v2/identity/identity_test.go b/v2/identity/identity_test.go new file mode 100644 index 0000000..d641858 --- /dev/null +++ b/v2/identity/identity_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package identity + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func TestMap(t *testing.T) { + assert.Equal(t, Of(2), F.Pipe1(1, Map(utils.Double))) +} + +func TestChain(t *testing.T) { + assert.Equal(t, Of(2), F.Pipe1(1, Chain(utils.Double))) +} + +func TestAp(t *testing.T) { + assert.Equal(t, Of(2), F.Pipe1(utils.Double, Ap[int, int](1))) +} diff --git a/v2/identity/monad.go b/v2/identity/monad.go new file mode 100644 index 0000000..b82e237 --- /dev/null +++ b/v2/identity/monad.go @@ -0,0 +1,43 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package identity + +import ( + "github.com/IBM/fp-go/v2/internal/monad" +) + +type identityMonad[A, B any] struct{} + +func (o *identityMonad[A, B]) Of(a A) A { + return Of[A](a) +} + +func (o *identityMonad[A, B]) Map(f func(A) B) func(A) B { + return Map[A, B](f) +} + +func (o *identityMonad[A, B]) Chain(f func(A) B) func(A) B { + return Chain[A, B](f) +} + +func (o *identityMonad[A, B]) Ap(fa A) func(func(A) B) B { + return Ap[B, A](fa) +} + +// Monad implements the monadic operations for [Option] +func Monad[A, B any]() monad.Monad[A, B, A, B, func(A) B] { + return &identityMonad[A, B]{} +} diff --git a/v2/internal/applicative/testing/law.go b/v2/internal/applicative/testing/law.go new file mode 100644 index 0000000..087e034 --- /dev/null +++ b/v2/internal/applicative/testing/law.go @@ -0,0 +1,283 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/applicative" + "github.com/IBM/fp-go/v2/internal/apply" + L "github.com/IBM/fp-go/v2/internal/apply/testing" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/pointed" + "github.com/stretchr/testify/assert" +) + +// Applicative identity law +// +// A.ap(A.of(a => a), fa) <-> fa +// +// Deprecated: use [ApplicativeAssertIdentity] +func AssertIdentity[HKTA, HKTAA, A any](t *testing.T, + eq E.Eq[HKTA], + + fof func(func(A) A) HKTAA, + + fap func(HKTAA, HKTA) HKTA, +) func(fa HKTA) bool { + // mark as test helper + t.Helper() + + return func(fa HKTA) bool { + + left := fap(fof(F.Identity[A]), fa) + right := fa + + return assert.True(t, eq.Equals(left, right), "Applicative identity") + } +} + +// Applicative identity law +// +// A.ap(A.of(a => a), fa) <-> fa +func ApplicativeAssertIdentity[HKTA, HKTFAA, A any](t *testing.T, + eq E.Eq[HKTA], + + ap applicative.Applicative[A, A, HKTA, HKTA, HKTFAA], + paa pointed.Pointed[func(A) A, HKTFAA], + +) func(fa HKTA) bool { + // mark as test helper + t.Helper() + + return func(fa HKTA) bool { + + left := ap.Ap(fa)(paa.Of(F.Identity[A])) + right := fa + + return assert.True(t, eq.Equals(left, right), "Applicative identity") + } +} + +// Applicative homomorphism law +// +// A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a)) +// +// Deprecated: use [ApplicativeAssertHomomorphism] +func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T, + eq E.Eq[HKTB], + + fofa func(A) HKTA, + fofb func(B) HKTB, + fofab func(func(A) B) HKTAB, + + fap func(HKTAB, HKTA) HKTB, + + ab func(A) B, +) func(a A) bool { + // mark as test helper + t.Helper() + + return func(a A) bool { + + left := fap(fofab(ab), fofa(a)) + right := fofb(ab(a)) + + return assert.True(t, eq.Equals(left, right), "Applicative homomorphism") + } +} + +// Applicative homomorphism law +// +// A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a)) +func ApplicativeAssertHomomorphism[HKTA, HKTB, HKTFAB, A, B any](t *testing.T, + eq E.Eq[HKTB], + + apab applicative.Applicative[A, B, HKTA, HKTB, HKTFAB], + pb pointed.Pointed[B, HKTB], + pfab pointed.Pointed[func(A) B, HKTFAB], + + ab func(A) B, +) func(a A) bool { + // mark as test helper + t.Helper() + + return func(a A) bool { + + left := apab.Ap(apab.Of(a))(pfab.Of(ab)) + right := pb.Of(ab(a)) + + return assert.True(t, eq.Equals(left, right), "Applicative homomorphism") + } +} + +// Applicative interchange law +// +// A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab) +// +// Deprecated: use [ApplicativeAssertInterchange] +func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T, + eq E.Eq[HKTB], + + fofa func(A) HKTA, + fofab func(func(A) B) HKTAB, + fofabb func(func(func(A) B) B) HKTABB, + + fapab func(HKTAB, HKTA) HKTB, + fapabb func(HKTABB, HKTAB) HKTB, + + ab func(A) B, +) func(a A) bool { + // mark as test helper + t.Helper() + + return func(a A) bool { + + fab := fofab(ab) + + left := fapab(fab, fofa(a)) + right := fapabb(fofabb(func(ab func(A) B) B { + return ab(a) + }), fab) + + return assert.True(t, eq.Equals(left, right), "Applicative homomorphism") + } +} + +// Applicative interchange law +// +// A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab) +func ApplicativeAssertInterchange[HKTA, HKTB, HKTFAB, HKTABB, A, B any](t *testing.T, + eq E.Eq[HKTB], + + apab applicative.Applicative[A, B, HKTA, HKTB, HKTFAB], + apabb applicative.Applicative[func(A) B, B, HKTFAB, HKTB, HKTABB], + pabb pointed.Pointed[func(func(A) B) B, HKTABB], + + ab func(A) B, +) func(a A) bool { + // mark as test helper + t.Helper() + + return func(a A) bool { + + fab := apabb.Of(ab) + + left := apab.Ap(apab.Of(a))(fab) + + right := apabb.Ap(fab)(pabb.Of(func(ab func(A) B) B { + return ab(a) + })) + + return assert.True(t, eq.Equals(left, right), "Applicative homomorphism") + } +} + +// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange' +// +// Deprecated: use [ApplicativeAssertLaws] instead +func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqb E.Eq[HKTB], + eqc E.Eq[HKTC], + + fofa func(A) HKTA, + fofb func(B) HKTB, + + fofaa func(func(A) A) HKTAA, + fofab func(func(A) B) HKTAB, + fofbc func(func(B) C) HKTBC, + fofabb func(func(func(A) B) B) HKTABB, + + faa func(HKTA, func(A) A) HKTA, + fab func(HKTA, func(A) B) HKTB, + fac func(HKTA, func(A) C) HKTC, + fbc func(HKTB, func(B) C) HKTC, + + fmap func(HKTBC, func(func(B) C) func(func(A) B) func(A) C) HKTABAC, + + fapaa func(HKTAA, HKTA) HKTA, + fapab func(HKTAB, HKTA) HKTB, + fapbc func(HKTBC, HKTB) HKTC, + fapac func(HKTAC, HKTA) HKTC, + + fapabb func(HKTABB, HKTAB) HKTB, + fapabac func(HKTABAC, HKTAB) HKTAC, + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + // mark as test helper + t.Helper() + + // apply laws + apply := L.AssertLaws(t, eqa, eqc, fofab, fofbc, faa, fab, fac, fbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc) + // applicative laws + identity := AssertIdentity(t, eqa, fofaa, fapaa) + homomorphism := AssertHomomorphism(t, eqb, fofa, fofb, fofab, fapab, ab) + interchange := AssertInterchange(t, eqb, fofa, fofab, fofabb, fapab, fapabb, ab) + + return func(a A) bool { + fa := fofa(a) + return apply(fa) && identity(fa) && homomorphism(a) && interchange(a) + } +} + +// ApplicativeAssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange' +func ApplicativeAssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqb E.Eq[HKTB], + eqc E.Eq[HKTC], + + fofb pointed.Pointed[B, HKTB], + + fofaa pointed.Pointed[func(A) A, HKTAA], + fofbc pointed.Pointed[func(B) C, HKTBC], + + fofabb pointed.Pointed[func(func(A) B) B, HKTABB], + + faa functor.Functor[A, A, HKTA, HKTA], + + fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC], + + fapaa applicative.Applicative[A, A, HKTA, HKTA, HKTAA], + fapab applicative.Applicative[A, B, HKTA, HKTB, HKTAB], + fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC], + fapac apply.Apply[A, C, HKTA, HKTC, HKTAC], + + fapabb applicative.Applicative[func(A) B, B, HKTAB, HKTB, HKTABB], + fapabac applicative.Applicative[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + // mark as test helper + t.Helper() + + // apply laws + apply := L.ApplyAssertLaws(t, eqa, eqc, applicative.ToPointed(fapabac), fofbc, faa, fmap, applicative.ToApply(fapab), fapbc, fapac, applicative.ToApply(fapabac), ab, bc) + // applicative laws + identity := ApplicativeAssertIdentity(t, eqa, fapaa, fofaa) + homomorphism := ApplicativeAssertHomomorphism(t, eqb, fapab, fofb, applicative.ToPointed(fapabb), ab) + interchange := ApplicativeAssertInterchange(t, eqb, fapab, fapabb, fofabb, ab) + + return func(a A) bool { + fa := fapaa.Of(a) + return apply(fa) && identity(fa) && homomorphism(a) && interchange(a) + } +} diff --git a/v2/internal/applicative/types.go b/v2/internal/applicative/types.go new file mode 100644 index 0000000..e8786ce --- /dev/null +++ b/v2/internal/applicative/types.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package applicative + +import ( + "github.com/IBM/fp-go/v2/internal/apply" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/pointed" +) + +type Applicative[A, B, HKTA, HKTB, HKTFAB any] interface { + apply.Apply[A, B, HKTA, HKTB, HKTFAB] + pointed.Pointed[A, HKTA] +} + +// ToFunctor converts from [Applicative] to [functor.Functor] +func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] { + return ap +} + +// ToApply converts from [Applicative] to [apply.Apply] +func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] { + return ap +} + +// ToPointed converts from [Applicative] to [pointed.Pointed] +func ToPointed[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) pointed.Pointed[A, HKTA] { + return ap +} diff --git a/v2/internal/apply/ap.go b/v2/internal/apply/ap.go new file mode 100644 index 0000000..82986ca --- /dev/null +++ b/v2/internal/apply/ap.go @@ -0,0 +1,147 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apply + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +func MonadAp[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any]( + fap func(HKTFGGAB, HKTFGA) HKTFGB, + fmap func(HKTFGAB, func(HKTGAB) func(HKTGA) HKTGB) HKTFGGAB, + gap func(HKTGAB, HKTGA) HKTGB, + + fab HKTFGAB, + fa HKTFGA) HKTFGB { + + return fap(fmap(fab, F.Bind1st(F.Bind1st[HKTGAB, HKTGA, HKTGB], gap)), fa) +} + +func Ap[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any]( + fap func(HKTFGA) func(HKTFGGAB) HKTFGB, + fmap func(func(HKTGAB) func(HKTGA) HKTGB) func(HKTFGAB) HKTFGGAB, + gap func(HKTGA) func(HKTGAB) HKTGB, + + fa HKTFGA) func(HKTFGAB) HKTFGB { + + return F.Flow2( + fmap(F.Flip(gap)), + fap(fa), + ) +} + +// func Ap[HKTGA, HKTGB, HKTGAB, HKTFGAB, HKTFGGAB, HKTFGA, HKTFGB any]( +// fap func(HKTFGA) func(HKTFGGAB) HKTFGB, +// fmap func(func(HKTGAB) func(HKTGA) HKTGB) func(HKTFGAB) HKTFGGAB, +// gap func(HKTGA) func(HKTGAB) HKTGB, + +// fa HKTFGA) func(HKTFGAB) HKTFGB { + +// return fap(fmap(F.Bind1st(F.Bind1st[HKTGAB, HKTGA, HKTGB], gap)), fa) +// } + +// export function ap( +// F: Apply, +// G: Apply +// ): (fa: HKT>) => (fab: HKT B>>) => HKT> { +// return (fa: HKT>) => (fab: HKT B>>): HKT> => +// F.ap( +// F.map(fab, (gab) => (ga: HKT) => G.ap(gab, ga)), +// fa +// ) +// } + +// function apFirst(A: Apply): (second: HKT) => (first: HKT) => HKT { +// return (second) => (first) => +// A.ap( +// A.map(first, (a) => () => a), +// second +// ) +// } + +// Functor.map: A>(fa: HKT, f: (a: A) => () => A) => HKT A> + +// Apply.ap: (fab: HKT A>, fa: HKT) => HKT + +func MonadApFirst[HKTGA, HKTGB, HKTGBA, A, B any]( + fap func(HKTGBA, HKTGB) HKTGA, + fmap func(HKTGA, func(A) func(B) A) HKTGBA, + + first HKTGA, + second HKTGB, +) HKTGA { + return fap( + fmap(first, F.Constant1[B, A]), + second, + ) +} + +func ApFirst[HKTGA, HKTGB, HKTGBA, A, B any]( + fap func(HKTGBA, HKTGB) HKTGA, + fmap func(HKTGA, func(A) func(B) A) HKTGBA, + + second HKTGB, +) func(HKTGA) HKTGA { + return func(first HKTGA) HKTGA { + return MonadApFirst(fap, fmap, first, second) + } +} + +func MonadApSecond[HKTGA, HKTGB, HKTGBB, A, B any]( + fap func(HKTGBB, HKTGB) HKTGB, + fmap func(HKTGA, func(A) func(B) B) HKTGBB, + + first HKTGA, + second HKTGB, +) HKTGB { + return fap( + fmap(first, F.Constant1[A](F.Identity[B])), + second, + ) +} + +func ApSecond[HKTGA, HKTGB, HKTGBB, A, B any]( + fap func(HKTGBB, HKTGB) HKTGB, + fmap func(HKTGA, func(A) func(B) B) HKTGBB, + + second HKTGB, +) func(HKTGA) HKTGB { + return func(first HKTGA) HKTGB { + return MonadApSecond(fap, fmap, first, second) + } +} + +func MonadApS[S1, S2, B, HKTBGBS2, HKTS1, HKTS2, HKTB any]( + fap func(HKTBGBS2, HKTB) HKTS2, + fmap func(HKTS1, func(S1) func(B) S2) HKTBGBS2, + fa HKTS1, + key func(B) func(S1) S2, + fb HKTB, +) HKTS2 { + return fap(fmap(fa, F.Flip(key)), fb) +} + +func ApS[S1, S2, B, HKTBGBS2, HKTS1, HKTS2, HKTB any]( + fap func(HKTB) func(HKTBGBS2) HKTS2, + fmap func(func(S1) func(B) S2) func(HKTS1) HKTBGBS2, + key func(B) func(S1) S2, + fb HKTB, +) func(HKTS1) HKTS2 { + return F.Flow2( + fmap(F.Flip(key)), + fap(fb), + ) +} diff --git a/v2/internal/apply/doc.go b/v2/internal/apply/doc.go new file mode 100644 index 0000000..35921b5 --- /dev/null +++ b/v2/internal/apply/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apply + +//go:generate go run ../.. apply --count 15 --filename gen.go diff --git a/v2/internal/apply/gen.go b/v2/internal/apply/gen.go new file mode 100644 index 0000000..aa7693b --- /dev/null +++ b/v2/internal/apply/gen.go @@ -0,0 +1,3085 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:04.4975371 +0100 CET m=+0.017695301 + +package apply + +import ( + F "github.com/IBM/fp-go/v2/function" + T "github.com/IBM/fp-go/v2/tuple" +) + +// tupleConstructor1 returns a curried version of [T.MakeTuple1] +func tupleConstructor1[T1 any]() func(T1) T.Tuple1[T1] { + return F.Curry1(T.MakeTuple1[T1]) +} + +// SequenceT1 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 1 higher higher kinded types and returns a higher kinded type of a [Tuple1] with the resolved values. +func SequenceT1[ + MAP ~func(func(T1) T.Tuple1[T1]) func(HKT_T1) HKT_TUPLE1, + T1, + HKT_T1, // HKT[T1] + HKT_TUPLE1 any, // HKT[Tuple1[T1]] +]( + fmap MAP, + t1 HKT_T1, +) HKT_TUPLE1 { + return F.Pipe1( + t1, + fmap(tupleConstructor1[T1]()), + ) +} + +// SequenceTuple1 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple1] of higher higher kinded types and returns a higher kinded type of a [Tuple1] with the resolved values. +func SequenceTuple1[ + MAP ~func(func(T1) T.Tuple1[T1]) func(HKT_T1) HKT_TUPLE1, + T1, + HKT_T1, // HKT[T1] + HKT_TUPLE1 any, // HKT[Tuple1[T1]] +]( + fmap MAP, + t T.Tuple1[HKT_T1], +) HKT_TUPLE1 { + return F.Pipe1( + t.F1, + fmap(tupleConstructor1[T1]()), + ) +} + +// TraverseTuple1 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple1] of base types and 1 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple1] with the resolved values. +func TraverseTuple1[ + MAP ~func(func(T1) T.Tuple1[T1]) func(HKT_T1) HKT_TUPLE1, + F1 ~func(A1) HKT_T1, + A1, T1, + HKT_T1, // HKT[T1] + HKT_TUPLE1 any, // HKT[Tuple1[T1]] +]( + fmap MAP, + f1 F1, + t T.Tuple1[A1], +) HKT_TUPLE1 { + return F.Pipe1( + f1(t.F1), + fmap(tupleConstructor1[T1]()), + ) +} + +// tupleConstructor2 returns a curried version of [T.MakeTuple2] +func tupleConstructor2[T1, T2 any]() func(T1) func(T2) T.Tuple2[T1, T2] { + return F.Curry2(T.MakeTuple2[T1, T2]) +} + +// SequenceT2 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 2 higher higher kinded types and returns a higher kinded type of a [Tuple2] with the resolved values. +func SequenceT2[ + MAP ~func(func(T1) func(T2) T.Tuple2[T1, T2]) func(HKT_T1) HKT_F_T2, + AP1 ~func(HKT_T2) func(HKT_F_T2) HKT_TUPLE2, + T1, + T2, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_F_T2, // HKT[func(T2) T.Tuple2[T1, T2]] + HKT_TUPLE2 any, // HKT[Tuple2[T1, T2]] +]( + fmap MAP, + fap1 AP1, + t1 HKT_T1, + t2 HKT_T2, +) HKT_TUPLE2 { + return F.Pipe2( + t1, + fmap(tupleConstructor2[T1, T2]()), + fap1(t2), + ) +} + +// SequenceTuple2 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple2] of higher higher kinded types and returns a higher kinded type of a [Tuple2] with the resolved values. +func SequenceTuple2[ + MAP ~func(func(T1) func(T2) T.Tuple2[T1, T2]) func(HKT_T1) HKT_F_T2, + AP1 ~func(HKT_T2) func(HKT_F_T2) HKT_TUPLE2, + T1, + T2, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_F_T2, // HKT[func(T2) T.Tuple2[T1, T2]] + HKT_TUPLE2 any, // HKT[Tuple2[T1, T2]] +]( + fmap MAP, + fap1 AP1, + t T.Tuple2[HKT_T1, HKT_T2], +) HKT_TUPLE2 { + return F.Pipe2( + t.F1, + fmap(tupleConstructor2[T1, T2]()), + fap1(t.F2), + ) +} + +// TraverseTuple2 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple2] of base types and 2 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple2] with the resolved values. +func TraverseTuple2[ + MAP ~func(func(T1) func(T2) T.Tuple2[T1, T2]) func(HKT_T1) HKT_F_T2, + AP1 ~func(HKT_T2) func(HKT_F_T2) HKT_TUPLE2, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + A1, T1, + A2, T2, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_F_T2, // HKT[func(T2) T.Tuple2[T1, T2]] + HKT_TUPLE2 any, // HKT[Tuple2[T1, T2]] +]( + fmap MAP, + fap1 AP1, + f1 F1, + f2 F2, + t T.Tuple2[A1, A2], +) HKT_TUPLE2 { + return F.Pipe2( + f1(t.F1), + fmap(tupleConstructor2[T1, T2]()), + fap1(f2(t.F2)), + ) +} + +// tupleConstructor3 returns a curried version of [T.MakeTuple3] +func tupleConstructor3[T1, T2, T3 any]() func(T1) func(T2) func(T3) T.Tuple3[T1, T2, T3] { + return F.Curry3(T.MakeTuple3[T1, T2, T3]) +} + +// SequenceT3 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 3 higher higher kinded types and returns a higher kinded type of a [Tuple3] with the resolved values. +func SequenceT3[ + MAP ~func(func(T1) func(T2) func(T3) T.Tuple3[T1, T2, T3]) func(HKT_T1) HKT_F_T2_T3, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3) HKT_F_T3, + AP2 ~func(HKT_T3) func(HKT_F_T3) HKT_TUPLE3, + T1, + T2, + T3, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_F_T2_T3, // HKT[func(T2) func(T3) T.Tuple3[T1, T2, T3]] + HKT_F_T3, // HKT[func(T3) T.Tuple3[T1, T2, T3]] + HKT_TUPLE3 any, // HKT[Tuple3[T1, T2, T3]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, +) HKT_TUPLE3 { + return F.Pipe3( + t1, + fmap(tupleConstructor3[T1, T2, T3]()), + fap1(t2), + fap2(t3), + ) +} + +// SequenceTuple3 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple3] of higher higher kinded types and returns a higher kinded type of a [Tuple3] with the resolved values. +func SequenceTuple3[ + MAP ~func(func(T1) func(T2) func(T3) T.Tuple3[T1, T2, T3]) func(HKT_T1) HKT_F_T2_T3, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3) HKT_F_T3, + AP2 ~func(HKT_T3) func(HKT_F_T3) HKT_TUPLE3, + T1, + T2, + T3, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_F_T2_T3, // HKT[func(T2) func(T3) T.Tuple3[T1, T2, T3]] + HKT_F_T3, // HKT[func(T3) T.Tuple3[T1, T2, T3]] + HKT_TUPLE3 any, // HKT[Tuple3[T1, T2, T3]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + t T.Tuple3[HKT_T1, HKT_T2, HKT_T3], +) HKT_TUPLE3 { + return F.Pipe3( + t.F1, + fmap(tupleConstructor3[T1, T2, T3]()), + fap1(t.F2), + fap2(t.F3), + ) +} + +// TraverseTuple3 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple3] of base types and 3 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple3] with the resolved values. +func TraverseTuple3[ + MAP ~func(func(T1) func(T2) func(T3) T.Tuple3[T1, T2, T3]) func(HKT_T1) HKT_F_T2_T3, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3) HKT_F_T3, + AP2 ~func(HKT_T3) func(HKT_F_T3) HKT_TUPLE3, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + A1, T1, + A2, T2, + A3, T3, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_F_T2_T3, // HKT[func(T2) func(T3) T.Tuple3[T1, T2, T3]] + HKT_F_T3, // HKT[func(T3) T.Tuple3[T1, T2, T3]] + HKT_TUPLE3 any, // HKT[Tuple3[T1, T2, T3]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + f1 F1, + f2 F2, + f3 F3, + t T.Tuple3[A1, A2, A3], +) HKT_TUPLE3 { + return F.Pipe3( + f1(t.F1), + fmap(tupleConstructor3[T1, T2, T3]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + ) +} + +// tupleConstructor4 returns a curried version of [T.MakeTuple4] +func tupleConstructor4[T1, T2, T3, T4 any]() func(T1) func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4] { + return F.Curry4(T.MakeTuple4[T1, T2, T3, T4]) +} + +// SequenceT4 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 4 higher higher kinded types and returns a higher kinded type of a [Tuple4] with the resolved values. +func SequenceT4[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]) func(HKT_T1) HKT_F_T2_T3_T4, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4) HKT_F_T3_T4, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4) HKT_F_T4, + AP3 ~func(HKT_T4) func(HKT_F_T4) HKT_TUPLE4, + T1, + T2, + T3, + T4, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_F_T2_T3_T4, // HKT[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_F_T3_T4, // HKT[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_F_T4, // HKT[func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_TUPLE4 any, // HKT[Tuple4[T1, T2, T3, T4]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, +) HKT_TUPLE4 { + return F.Pipe4( + t1, + fmap(tupleConstructor4[T1, T2, T3, T4]()), + fap1(t2), + fap2(t3), + fap3(t4), + ) +} + +// SequenceTuple4 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple4] of higher higher kinded types and returns a higher kinded type of a [Tuple4] with the resolved values. +func SequenceTuple4[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]) func(HKT_T1) HKT_F_T2_T3_T4, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4) HKT_F_T3_T4, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4) HKT_F_T4, + AP3 ~func(HKT_T4) func(HKT_F_T4) HKT_TUPLE4, + T1, + T2, + T3, + T4, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_F_T2_T3_T4, // HKT[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_F_T3_T4, // HKT[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_F_T4, // HKT[func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_TUPLE4 any, // HKT[Tuple4[T1, T2, T3, T4]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + t T.Tuple4[HKT_T1, HKT_T2, HKT_T3, HKT_T4], +) HKT_TUPLE4 { + return F.Pipe4( + t.F1, + fmap(tupleConstructor4[T1, T2, T3, T4]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + ) +} + +// TraverseTuple4 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple4] of base types and 4 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple4] with the resolved values. +func TraverseTuple4[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]) func(HKT_T1) HKT_F_T2_T3_T4, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4) HKT_F_T3_T4, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4) HKT_F_T4, + AP3 ~func(HKT_T4) func(HKT_F_T4) HKT_TUPLE4, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_F_T2_T3_T4, // HKT[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_F_T3_T4, // HKT[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_F_T4, // HKT[func(T4) T.Tuple4[T1, T2, T3, T4]] + HKT_TUPLE4 any, // HKT[Tuple4[T1, T2, T3, T4]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + t T.Tuple4[A1, A2, A3, A4], +) HKT_TUPLE4 { + return F.Pipe4( + f1(t.F1), + fmap(tupleConstructor4[T1, T2, T3, T4]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + ) +} + +// tupleConstructor5 returns a curried version of [T.MakeTuple5] +func tupleConstructor5[T1, T2, T3, T4, T5 any]() func(T1) func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5] { + return F.Curry5(T.MakeTuple5[T1, T2, T3, T4, T5]) +} + +// SequenceT5 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 5 higher higher kinded types and returns a higher kinded type of a [Tuple5] with the resolved values. +func SequenceT5[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]) func(HKT_T1) HKT_F_T2_T3_T4_T5, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5) HKT_F_T3_T4_T5, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5) HKT_F_T4_T5, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5) HKT_F_T5, + AP4 ~func(HKT_T5) func(HKT_F_T5) HKT_TUPLE5, + T1, + T2, + T3, + T4, + T5, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_F_T2_T3_T4_T5, // HKT[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T3_T4_T5, // HKT[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T4_T5, // HKT[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T5, // HKT[func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_TUPLE5 any, // HKT[Tuple5[T1, T2, T3, T4, T5]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, +) HKT_TUPLE5 { + return F.Pipe5( + t1, + fmap(tupleConstructor5[T1, T2, T3, T4, T5]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + ) +} + +// SequenceTuple5 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple5] of higher higher kinded types and returns a higher kinded type of a [Tuple5] with the resolved values. +func SequenceTuple5[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]) func(HKT_T1) HKT_F_T2_T3_T4_T5, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5) HKT_F_T3_T4_T5, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5) HKT_F_T4_T5, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5) HKT_F_T5, + AP4 ~func(HKT_T5) func(HKT_F_T5) HKT_TUPLE5, + T1, + T2, + T3, + T4, + T5, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_F_T2_T3_T4_T5, // HKT[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T3_T4_T5, // HKT[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T4_T5, // HKT[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T5, // HKT[func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_TUPLE5 any, // HKT[Tuple5[T1, T2, T3, T4, T5]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + t T.Tuple5[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5], +) HKT_TUPLE5 { + return F.Pipe5( + t.F1, + fmap(tupleConstructor5[T1, T2, T3, T4, T5]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + ) +} + +// TraverseTuple5 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple5] of base types and 5 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple5] with the resolved values. +func TraverseTuple5[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]) func(HKT_T1) HKT_F_T2_T3_T4_T5, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5) HKT_F_T3_T4_T5, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5) HKT_F_T4_T5, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5) HKT_F_T5, + AP4 ~func(HKT_T5) func(HKT_F_T5) HKT_TUPLE5, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_F_T2_T3_T4_T5, // HKT[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T3_T4_T5, // HKT[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T4_T5, // HKT[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_F_T5, // HKT[func(T5) T.Tuple5[T1, T2, T3, T4, T5]] + HKT_TUPLE5 any, // HKT[Tuple5[T1, T2, T3, T4, T5]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + t T.Tuple5[A1, A2, A3, A4, A5], +) HKT_TUPLE5 { + return F.Pipe5( + f1(t.F1), + fmap(tupleConstructor5[T1, T2, T3, T4, T5]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + ) +} + +// tupleConstructor6 returns a curried version of [T.MakeTuple6] +func tupleConstructor6[T1, T2, T3, T4, T5, T6 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6] { + return F.Curry6(T.MakeTuple6[T1, T2, T3, T4, T5, T6]) +} + +// SequenceT6 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 6 higher higher kinded types and returns a higher kinded type of a [Tuple6] with the resolved values. +func SequenceT6[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6) HKT_F_T3_T4_T5_T6, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6) HKT_F_T4_T5_T6, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6) HKT_F_T5_T6, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6) HKT_F_T6, + AP5 ~func(HKT_T6) func(HKT_F_T6) HKT_TUPLE6, + T1, + T2, + T3, + T4, + T5, + T6, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_F_T2_T3_T4_T5_T6, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T3_T4_T5_T6, // HKT[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T4_T5_T6, // HKT[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T5_T6, // HKT[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T6, // HKT[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_TUPLE6 any, // HKT[Tuple6[T1, T2, T3, T4, T5, T6]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, +) HKT_TUPLE6 { + return F.Pipe6( + t1, + fmap(tupleConstructor6[T1, T2, T3, T4, T5, T6]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + ) +} + +// SequenceTuple6 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple6] of higher higher kinded types and returns a higher kinded type of a [Tuple6] with the resolved values. +func SequenceTuple6[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6) HKT_F_T3_T4_T5_T6, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6) HKT_F_T4_T5_T6, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6) HKT_F_T5_T6, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6) HKT_F_T6, + AP5 ~func(HKT_T6) func(HKT_F_T6) HKT_TUPLE6, + T1, + T2, + T3, + T4, + T5, + T6, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_F_T2_T3_T4_T5_T6, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T3_T4_T5_T6, // HKT[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T4_T5_T6, // HKT[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T5_T6, // HKT[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T6, // HKT[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_TUPLE6 any, // HKT[Tuple6[T1, T2, T3, T4, T5, T6]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + t T.Tuple6[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6], +) HKT_TUPLE6 { + return F.Pipe6( + t.F1, + fmap(tupleConstructor6[T1, T2, T3, T4, T5, T6]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + ) +} + +// TraverseTuple6 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple6] of base types and 6 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple6] with the resolved values. +func TraverseTuple6[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6) HKT_F_T3_T4_T5_T6, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6) HKT_F_T4_T5_T6, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6) HKT_F_T5_T6, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6) HKT_F_T6, + AP5 ~func(HKT_T6) func(HKT_F_T6) HKT_TUPLE6, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_F_T2_T3_T4_T5_T6, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T3_T4_T5_T6, // HKT[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T4_T5_T6, // HKT[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T5_T6, // HKT[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_F_T6, // HKT[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]] + HKT_TUPLE6 any, // HKT[Tuple6[T1, T2, T3, T4, T5, T6]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + t T.Tuple6[A1, A2, A3, A4, A5, A6], +) HKT_TUPLE6 { + return F.Pipe6( + f1(t.F1), + fmap(tupleConstructor6[T1, T2, T3, T4, T5, T6]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + ) +} + +// tupleConstructor7 returns a curried version of [T.MakeTuple7] +func tupleConstructor7[T1, T2, T3, T4, T5, T6, T7 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return F.Curry7(T.MakeTuple7[T1, T2, T3, T4, T5, T6, T7]) +} + +// SequenceT7 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 7 higher higher kinded types and returns a higher kinded type of a [Tuple7] with the resolved values. +func SequenceT7[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7) HKT_F_T3_T4_T5_T6_T7, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7) HKT_F_T4_T5_T6_T7, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7) HKT_F_T5_T6_T7, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7) HKT_F_T6_T7, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7) HKT_F_T7, + AP6 ~func(HKT_T7) func(HKT_F_T7) HKT_TUPLE7, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_F_T2_T3_T4_T5_T6_T7, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T3_T4_T5_T6_T7, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T4_T5_T6_T7, // HKT[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T5_T6_T7, // HKT[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T6_T7, // HKT[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T7, // HKT[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_TUPLE7 any, // HKT[Tuple7[T1, T2, T3, T4, T5, T6, T7]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, +) HKT_TUPLE7 { + return F.Pipe7( + t1, + fmap(tupleConstructor7[T1, T2, T3, T4, T5, T6, T7]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + ) +} + +// SequenceTuple7 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple7] of higher higher kinded types and returns a higher kinded type of a [Tuple7] with the resolved values. +func SequenceTuple7[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7) HKT_F_T3_T4_T5_T6_T7, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7) HKT_F_T4_T5_T6_T7, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7) HKT_F_T5_T6_T7, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7) HKT_F_T6_T7, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7) HKT_F_T7, + AP6 ~func(HKT_T7) func(HKT_F_T7) HKT_TUPLE7, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_F_T2_T3_T4_T5_T6_T7, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T3_T4_T5_T6_T7, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T4_T5_T6_T7, // HKT[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T5_T6_T7, // HKT[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T6_T7, // HKT[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T7, // HKT[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_TUPLE7 any, // HKT[Tuple7[T1, T2, T3, T4, T5, T6, T7]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + t T.Tuple7[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7], +) HKT_TUPLE7 { + return F.Pipe7( + t.F1, + fmap(tupleConstructor7[T1, T2, T3, T4, T5, T6, T7]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + ) +} + +// TraverseTuple7 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple7] of base types and 7 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple7] with the resolved values. +func TraverseTuple7[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7) HKT_F_T3_T4_T5_T6_T7, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7) HKT_F_T4_T5_T6_T7, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7) HKT_F_T5_T6_T7, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7) HKT_F_T6_T7, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7) HKT_F_T7, + AP6 ~func(HKT_T7) func(HKT_F_T7) HKT_TUPLE7, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_F_T2_T3_T4_T5_T6_T7, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T3_T4_T5_T6_T7, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T4_T5_T6_T7, // HKT[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T5_T6_T7, // HKT[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T6_T7, // HKT[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_F_T7, // HKT[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] + HKT_TUPLE7 any, // HKT[Tuple7[T1, T2, T3, T4, T5, T6, T7]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + t T.Tuple7[A1, A2, A3, A4, A5, A6, A7], +) HKT_TUPLE7 { + return F.Pipe7( + f1(t.F1), + fmap(tupleConstructor7[T1, T2, T3, T4, T5, T6, T7]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + ) +} + +// tupleConstructor8 returns a curried version of [T.MakeTuple8] +func tupleConstructor8[T1, T2, T3, T4, T5, T6, T7, T8 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return F.Curry8(T.MakeTuple8[T1, T2, T3, T4, T5, T6, T7, T8]) +} + +// SequenceT8 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 8 higher higher kinded types and returns a higher kinded type of a [Tuple8] with the resolved values. +func SequenceT8[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8) HKT_F_T3_T4_T5_T6_T7_T8, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8) HKT_F_T4_T5_T6_T7_T8, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8) HKT_F_T5_T6_T7_T8, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8) HKT_F_T6_T7_T8, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8) HKT_F_T7_T8, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8) HKT_F_T8, + AP7 ~func(HKT_T8) func(HKT_F_T8) HKT_TUPLE8, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_F_T2_T3_T4_T5_T6_T7_T8, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T3_T4_T5_T6_T7_T8, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T4_T5_T6_T7_T8, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T5_T6_T7_T8, // HKT[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T6_T7_T8, // HKT[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T7_T8, // HKT[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T8, // HKT[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_TUPLE8 any, // HKT[Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, + t8 HKT_T8, +) HKT_TUPLE8 { + return F.Pipe8( + t1, + fmap(tupleConstructor8[T1, T2, T3, T4, T5, T6, T7, T8]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + fap7(t8), + ) +} + +// SequenceTuple8 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple8] of higher higher kinded types and returns a higher kinded type of a [Tuple8] with the resolved values. +func SequenceTuple8[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8) HKT_F_T3_T4_T5_T6_T7_T8, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8) HKT_F_T4_T5_T6_T7_T8, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8) HKT_F_T5_T6_T7_T8, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8) HKT_F_T6_T7_T8, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8) HKT_F_T7_T8, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8) HKT_F_T8, + AP7 ~func(HKT_T8) func(HKT_F_T8) HKT_TUPLE8, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_F_T2_T3_T4_T5_T6_T7_T8, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T3_T4_T5_T6_T7_T8, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T4_T5_T6_T7_T8, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T5_T6_T7_T8, // HKT[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T6_T7_T8, // HKT[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T7_T8, // HKT[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T8, // HKT[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_TUPLE8 any, // HKT[Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + t T.Tuple8[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7, HKT_T8], +) HKT_TUPLE8 { + return F.Pipe8( + t.F1, + fmap(tupleConstructor8[T1, T2, T3, T4, T5, T6, T7, T8]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + fap7(t.F8), + ) +} + +// TraverseTuple8 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple8] of base types and 8 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple8] with the resolved values. +func TraverseTuple8[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8) HKT_F_T3_T4_T5_T6_T7_T8, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8) HKT_F_T4_T5_T6_T7_T8, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8) HKT_F_T5_T6_T7_T8, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8) HKT_F_T6_T7_T8, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8) HKT_F_T7_T8, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8) HKT_F_T8, + AP7 ~func(HKT_T8) func(HKT_F_T8) HKT_TUPLE8, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + F8 ~func(A8) HKT_T8, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + A8, T8, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_F_T2_T3_T4_T5_T6_T7_T8, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T3_T4_T5_T6_T7_T8, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T4_T5_T6_T7_T8, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T5_T6_T7_T8, // HKT[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T6_T7_T8, // HKT[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T7_T8, // HKT[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_F_T8, // HKT[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] + HKT_TUPLE8 any, // HKT[Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + f8 F8, + t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8], +) HKT_TUPLE8 { + return F.Pipe8( + f1(t.F1), + fmap(tupleConstructor8[T1, T2, T3, T4, T5, T6, T7, T8]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + fap7(f8(t.F8)), + ) +} + +// tupleConstructor9 returns a curried version of [T.MakeTuple9] +func tupleConstructor9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return F.Curry9(T.MakeTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) +} + +// SequenceT9 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 9 higher higher kinded types and returns a higher kinded type of a [Tuple9] with the resolved values. +func SequenceT9[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9) HKT_F_T3_T4_T5_T6_T7_T8_T9, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9) HKT_F_T4_T5_T6_T7_T8_T9, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9) HKT_F_T5_T6_T7_T8_T9, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9) HKT_F_T6_T7_T8_T9, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9) HKT_F_T7_T8_T9, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9) HKT_F_T8_T9, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9) HKT_F_T9, + AP8 ~func(HKT_T9) func(HKT_F_T9) HKT_TUPLE9, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T3_T4_T5_T6_T7_T8_T9, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T4_T5_T6_T7_T8_T9, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T5_T6_T7_T8_T9, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T6_T7_T8_T9, // HKT[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T7_T8_T9, // HKT[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T8_T9, // HKT[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T9, // HKT[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_TUPLE9 any, // HKT[Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, + t8 HKT_T8, + t9 HKT_T9, +) HKT_TUPLE9 { + return F.Pipe9( + t1, + fmap(tupleConstructor9[T1, T2, T3, T4, T5, T6, T7, T8, T9]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + fap7(t8), + fap8(t9), + ) +} + +// SequenceTuple9 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple9] of higher higher kinded types and returns a higher kinded type of a [Tuple9] with the resolved values. +func SequenceTuple9[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9) HKT_F_T3_T4_T5_T6_T7_T8_T9, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9) HKT_F_T4_T5_T6_T7_T8_T9, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9) HKT_F_T5_T6_T7_T8_T9, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9) HKT_F_T6_T7_T8_T9, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9) HKT_F_T7_T8_T9, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9) HKT_F_T8_T9, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9) HKT_F_T9, + AP8 ~func(HKT_T9) func(HKT_F_T9) HKT_TUPLE9, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T3_T4_T5_T6_T7_T8_T9, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T4_T5_T6_T7_T8_T9, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T5_T6_T7_T8_T9, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T6_T7_T8_T9, // HKT[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T7_T8_T9, // HKT[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T8_T9, // HKT[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T9, // HKT[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_TUPLE9 any, // HKT[Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + t T.Tuple9[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7, HKT_T8, HKT_T9], +) HKT_TUPLE9 { + return F.Pipe9( + t.F1, + fmap(tupleConstructor9[T1, T2, T3, T4, T5, T6, T7, T8, T9]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + fap7(t.F8), + fap8(t.F9), + ) +} + +// TraverseTuple9 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple9] of base types and 9 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple9] with the resolved values. +func TraverseTuple9[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9) HKT_F_T3_T4_T5_T6_T7_T8_T9, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9) HKT_F_T4_T5_T6_T7_T8_T9, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9) HKT_F_T5_T6_T7_T8_T9, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9) HKT_F_T6_T7_T8_T9, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9) HKT_F_T7_T8_T9, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9) HKT_F_T8_T9, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9) HKT_F_T9, + AP8 ~func(HKT_T9) func(HKT_F_T9) HKT_TUPLE9, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + F8 ~func(A8) HKT_T8, + F9 ~func(A9) HKT_T9, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + A8, T8, + A9, T9, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T3_T4_T5_T6_T7_T8_T9, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T4_T5_T6_T7_T8_T9, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T5_T6_T7_T8_T9, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T6_T7_T8_T9, // HKT[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T7_T8_T9, // HKT[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T8_T9, // HKT[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_F_T9, // HKT[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] + HKT_TUPLE9 any, // HKT[Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + f8 F8, + f9 F9, + t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9], +) HKT_TUPLE9 { + return F.Pipe9( + f1(t.F1), + fmap(tupleConstructor9[T1, T2, T3, T4, T5, T6, T7, T8, T9]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + fap7(f8(t.F8)), + fap8(f9(t.F9)), + ) +} + +// tupleConstructor10 returns a curried version of [T.MakeTuple10] +func tupleConstructor10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return F.Curry10(T.MakeTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) +} + +// SequenceT10 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 10 higher higher kinded types and returns a higher kinded type of a [Tuple10] with the resolved values. +func SequenceT10[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10) HKT_F_T4_T5_T6_T7_T8_T9_T10, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10) HKT_F_T5_T6_T7_T8_T9_T10, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10) HKT_F_T6_T7_T8_T9_T10, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10) HKT_F_T7_T8_T9_T10, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10) HKT_F_T8_T9_T10, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10) HKT_F_T9_T10, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10) HKT_F_T10, + AP9 ~func(HKT_T10) func(HKT_F_T10) HKT_TUPLE10, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T5_T6_T7_T8_T9_T10, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T6_T7_T8_T9_T10, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T7_T8_T9_T10, // HKT[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T8_T9_T10, // HKT[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T9_T10, // HKT[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T10, // HKT[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_TUPLE10 any, // HKT[Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, + t8 HKT_T8, + t9 HKT_T9, + t10 HKT_T10, +) HKT_TUPLE10 { + return F.Pipe10( + t1, + fmap(tupleConstructor10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + fap7(t8), + fap8(t9), + fap9(t10), + ) +} + +// SequenceTuple10 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple10] of higher higher kinded types and returns a higher kinded type of a [Tuple10] with the resolved values. +func SequenceTuple10[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10) HKT_F_T4_T5_T6_T7_T8_T9_T10, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10) HKT_F_T5_T6_T7_T8_T9_T10, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10) HKT_F_T6_T7_T8_T9_T10, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10) HKT_F_T7_T8_T9_T10, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10) HKT_F_T8_T9_T10, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10) HKT_F_T9_T10, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10) HKT_F_T10, + AP9 ~func(HKT_T10) func(HKT_F_T10) HKT_TUPLE10, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T5_T6_T7_T8_T9_T10, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T6_T7_T8_T9_T10, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T7_T8_T9_T10, // HKT[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T8_T9_T10, // HKT[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T9_T10, // HKT[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T10, // HKT[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_TUPLE10 any, // HKT[Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + t T.Tuple10[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7, HKT_T8, HKT_T9, HKT_T10], +) HKT_TUPLE10 { + return F.Pipe10( + t.F1, + fmap(tupleConstructor10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + fap7(t.F8), + fap8(t.F9), + fap9(t.F10), + ) +} + +// TraverseTuple10 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple10] of base types and 10 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple10] with the resolved values. +func TraverseTuple10[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10) HKT_F_T4_T5_T6_T7_T8_T9_T10, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10) HKT_F_T5_T6_T7_T8_T9_T10, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10) HKT_F_T6_T7_T8_T9_T10, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10) HKT_F_T7_T8_T9_T10, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10) HKT_F_T8_T9_T10, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10) HKT_F_T9_T10, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10) HKT_F_T10, + AP9 ~func(HKT_T10) func(HKT_F_T10) HKT_TUPLE10, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + F8 ~func(A8) HKT_T8, + F9 ~func(A9) HKT_T9, + F10 ~func(A10) HKT_T10, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + A8, T8, + A9, T9, + A10, T10, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T4_T5_T6_T7_T8_T9_T10, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T5_T6_T7_T8_T9_T10, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T6_T7_T8_T9_T10, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T7_T8_T9_T10, // HKT[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T8_T9_T10, // HKT[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T9_T10, // HKT[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_F_T10, // HKT[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] + HKT_TUPLE10 any, // HKT[Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + f8 F8, + f9 F9, + f10 F10, + t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10], +) HKT_TUPLE10 { + return F.Pipe10( + f1(t.F1), + fmap(tupleConstructor10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + fap7(f8(t.F8)), + fap8(f9(t.F9)), + fap9(f10(t.F10)), + ) +} + +// tupleConstructor11 returns a curried version of [T.MakeTuple11] +func tupleConstructor11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { + return F.Curry11(T.MakeTuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) +} + +// SequenceT11 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 11 higher higher kinded types and returns a higher kinded type of a [Tuple11] with the resolved values. +func SequenceT11[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T5_T6_T7_T8_T9_T10_T11, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11) HKT_F_T6_T7_T8_T9_T10_T11, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11) HKT_F_T7_T8_T9_T10_T11, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11) HKT_F_T8_T9_T10_T11, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11) HKT_F_T9_T10_T11, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11) HKT_F_T10_T11, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11) HKT_F_T11, + AP10 ~func(HKT_T11) func(HKT_F_T11) HKT_TUPLE11, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T6_T7_T8_T9_T10_T11, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T7_T8_T9_T10_T11, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T8_T9_T10_T11, // HKT[func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T9_T10_T11, // HKT[func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T10_T11, // HKT[func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T11, // HKT[func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_TUPLE11 any, // HKT[Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, + t8 HKT_T8, + t9 HKT_T9, + t10 HKT_T10, + t11 HKT_T11, +) HKT_TUPLE11 { + return F.Pipe11( + t1, + fmap(tupleConstructor11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + fap7(t8), + fap8(t9), + fap9(t10), + fap10(t11), + ) +} + +// SequenceTuple11 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple11] of higher higher kinded types and returns a higher kinded type of a [Tuple11] with the resolved values. +func SequenceTuple11[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T5_T6_T7_T8_T9_T10_T11, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11) HKT_F_T6_T7_T8_T9_T10_T11, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11) HKT_F_T7_T8_T9_T10_T11, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11) HKT_F_T8_T9_T10_T11, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11) HKT_F_T9_T10_T11, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11) HKT_F_T10_T11, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11) HKT_F_T11, + AP10 ~func(HKT_T11) func(HKT_F_T11) HKT_TUPLE11, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T6_T7_T8_T9_T10_T11, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T7_T8_T9_T10_T11, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T8_T9_T10_T11, // HKT[func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T9_T10_T11, // HKT[func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T10_T11, // HKT[func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T11, // HKT[func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_TUPLE11 any, // HKT[Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + t T.Tuple11[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7, HKT_T8, HKT_T9, HKT_T10, HKT_T11], +) HKT_TUPLE11 { + return F.Pipe11( + t.F1, + fmap(tupleConstructor11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + fap7(t.F8), + fap8(t.F9), + fap9(t.F10), + fap10(t.F11), + ) +} + +// TraverseTuple11 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple11] of base types and 11 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple11] with the resolved values. +func TraverseTuple11[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11) HKT_F_T5_T6_T7_T8_T9_T10_T11, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11) HKT_F_T6_T7_T8_T9_T10_T11, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11) HKT_F_T7_T8_T9_T10_T11, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11) HKT_F_T8_T9_T10_T11, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11) HKT_F_T9_T10_T11, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11) HKT_F_T10_T11, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11) HKT_F_T11, + AP10 ~func(HKT_T11) func(HKT_F_T11) HKT_TUPLE11, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + F8 ~func(A8) HKT_T8, + F9 ~func(A9) HKT_T9, + F10 ~func(A10) HKT_T10, + F11 ~func(A11) HKT_T11, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + A8, T8, + A9, T9, + A10, T10, + A11, T11, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T5_T6_T7_T8_T9_T10_T11, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T6_T7_T8_T9_T10_T11, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T7_T8_T9_T10_T11, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T8_T9_T10_T11, // HKT[func(T8) func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T9_T10_T11, // HKT[func(T9) func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T10_T11, // HKT[func(T10) func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_F_T11, // HKT[func(T11) T.Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] + HKT_TUPLE11 any, // HKT[Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + f8 F8, + f9 F9, + f10 F10, + f11 F11, + t T.Tuple11[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11], +) HKT_TUPLE11 { + return F.Pipe11( + f1(t.F1), + fmap(tupleConstructor11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + fap7(f8(t.F8)), + fap8(f9(t.F9)), + fap9(f10(t.F10)), + fap10(f11(t.F11)), + ) +} + +// tupleConstructor12 returns a curried version of [T.MakeTuple12] +func tupleConstructor12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { + return F.Curry12(T.MakeTuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) +} + +// SequenceT12 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 12 higher higher kinded types and returns a higher kinded type of a [Tuple12] with the resolved values. +func SequenceT12[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T6_T7_T8_T9_T10_T11_T12, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12) HKT_F_T7_T8_T9_T10_T11_T12, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12) HKT_F_T8_T9_T10_T11_T12, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12) HKT_F_T9_T10_T11_T12, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12) HKT_F_T10_T11_T12, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12) HKT_F_T11_T12, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12) HKT_F_T12, + AP11 ~func(HKT_T12) func(HKT_F_T12) HKT_TUPLE12, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T7_T8_T9_T10_T11_T12, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T8_T9_T10_T11_T12, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T9_T10_T11_T12, // HKT[func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T10_T11_T12, // HKT[func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T11_T12, // HKT[func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T12, // HKT[func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_TUPLE12 any, // HKT[Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, + t8 HKT_T8, + t9 HKT_T9, + t10 HKT_T10, + t11 HKT_T11, + t12 HKT_T12, +) HKT_TUPLE12 { + return F.Pipe12( + t1, + fmap(tupleConstructor12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + fap7(t8), + fap8(t9), + fap9(t10), + fap10(t11), + fap11(t12), + ) +} + +// SequenceTuple12 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple12] of higher higher kinded types and returns a higher kinded type of a [Tuple12] with the resolved values. +func SequenceTuple12[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T6_T7_T8_T9_T10_T11_T12, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12) HKT_F_T7_T8_T9_T10_T11_T12, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12) HKT_F_T8_T9_T10_T11_T12, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12) HKT_F_T9_T10_T11_T12, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12) HKT_F_T10_T11_T12, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12) HKT_F_T11_T12, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12) HKT_F_T12, + AP11 ~func(HKT_T12) func(HKT_F_T12) HKT_TUPLE12, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T7_T8_T9_T10_T11_T12, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T8_T9_T10_T11_T12, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T9_T10_T11_T12, // HKT[func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T10_T11_T12, // HKT[func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T11_T12, // HKT[func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T12, // HKT[func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_TUPLE12 any, // HKT[Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + t T.Tuple12[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7, HKT_T8, HKT_T9, HKT_T10, HKT_T11, HKT_T12], +) HKT_TUPLE12 { + return F.Pipe12( + t.F1, + fmap(tupleConstructor12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + fap7(t.F8), + fap8(t.F9), + fap9(t.F10), + fap10(t.F11), + fap11(t.F12), + ) +} + +// TraverseTuple12 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple12] of base types and 12 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple12] with the resolved values. +func TraverseTuple12[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12) HKT_F_T6_T7_T8_T9_T10_T11_T12, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12) HKT_F_T7_T8_T9_T10_T11_T12, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12) HKT_F_T8_T9_T10_T11_T12, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12) HKT_F_T9_T10_T11_T12, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12) HKT_F_T10_T11_T12, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12) HKT_F_T11_T12, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12) HKT_F_T12, + AP11 ~func(HKT_T12) func(HKT_F_T12) HKT_TUPLE12, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + F8 ~func(A8) HKT_T8, + F9 ~func(A9) HKT_T9, + F10 ~func(A10) HKT_T10, + F11 ~func(A11) HKT_T11, + F12 ~func(A12) HKT_T12, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + A8, T8, + A9, T9, + A10, T10, + A11, T11, + A12, T12, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T6_T7_T8_T9_T10_T11_T12, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T7_T8_T9_T10_T11_T12, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T8_T9_T10_T11_T12, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T9_T10_T11_T12, // HKT[func(T9) func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T10_T11_T12, // HKT[func(T10) func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T11_T12, // HKT[func(T11) func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_F_T12, // HKT[func(T12) T.Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] + HKT_TUPLE12 any, // HKT[Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + f8 F8, + f9 F9, + f10 F10, + f11 F11, + f12 F12, + t T.Tuple12[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12], +) HKT_TUPLE12 { + return F.Pipe12( + f1(t.F1), + fmap(tupleConstructor12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + fap7(f8(t.F8)), + fap8(f9(t.F9)), + fap9(f10(t.F10)), + fap10(f11(t.F11)), + fap11(f12(t.F12)), + ) +} + +// tupleConstructor13 returns a curried version of [T.MakeTuple13] +func tupleConstructor13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { + return F.Curry13(T.MakeTuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) +} + +// SequenceT13 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 13 higher higher kinded types and returns a higher kinded type of a [Tuple13] with the resolved values. +func SequenceT13[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T7_T8_T9_T10_T11_T12_T13, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13) HKT_F_T8_T9_T10_T11_T12_T13, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13) HKT_F_T9_T10_T11_T12_T13, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13) HKT_F_T10_T11_T12_T13, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13) HKT_F_T11_T12_T13, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13) HKT_F_T12_T13, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13) HKT_F_T13, + AP12 ~func(HKT_T13) func(HKT_F_T13) HKT_TUPLE13, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + T13, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T8_T9_T10_T11_T12_T13, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T9_T10_T11_T12_T13, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T10_T11_T12_T13, // HKT[func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T11_T12_T13, // HKT[func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T12_T13, // HKT[func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T13, // HKT[func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_TUPLE13 any, // HKT[Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, + t8 HKT_T8, + t9 HKT_T9, + t10 HKT_T10, + t11 HKT_T11, + t12 HKT_T12, + t13 HKT_T13, +) HKT_TUPLE13 { + return F.Pipe13( + t1, + fmap(tupleConstructor13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + fap7(t8), + fap8(t9), + fap9(t10), + fap10(t11), + fap11(t12), + fap12(t13), + ) +} + +// SequenceTuple13 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple13] of higher higher kinded types and returns a higher kinded type of a [Tuple13] with the resolved values. +func SequenceTuple13[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T7_T8_T9_T10_T11_T12_T13, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13) HKT_F_T8_T9_T10_T11_T12_T13, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13) HKT_F_T9_T10_T11_T12_T13, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13) HKT_F_T10_T11_T12_T13, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13) HKT_F_T11_T12_T13, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13) HKT_F_T12_T13, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13) HKT_F_T13, + AP12 ~func(HKT_T13) func(HKT_F_T13) HKT_TUPLE13, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + T13, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T8_T9_T10_T11_T12_T13, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T9_T10_T11_T12_T13, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T10_T11_T12_T13, // HKT[func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T11_T12_T13, // HKT[func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T12_T13, // HKT[func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T13, // HKT[func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_TUPLE13 any, // HKT[Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + t T.Tuple13[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7, HKT_T8, HKT_T9, HKT_T10, HKT_T11, HKT_T12, HKT_T13], +) HKT_TUPLE13 { + return F.Pipe13( + t.F1, + fmap(tupleConstructor13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + fap7(t.F8), + fap8(t.F9), + fap9(t.F10), + fap10(t.F11), + fap11(t.F12), + fap12(t.F13), + ) +} + +// TraverseTuple13 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple13] of base types and 13 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple13] with the resolved values. +func TraverseTuple13[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13) HKT_F_T7_T8_T9_T10_T11_T12_T13, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13) HKT_F_T8_T9_T10_T11_T12_T13, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13) HKT_F_T9_T10_T11_T12_T13, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13) HKT_F_T10_T11_T12_T13, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13) HKT_F_T11_T12_T13, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13) HKT_F_T12_T13, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13) HKT_F_T13, + AP12 ~func(HKT_T13) func(HKT_F_T13) HKT_TUPLE13, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + F8 ~func(A8) HKT_T8, + F9 ~func(A9) HKT_T9, + F10 ~func(A10) HKT_T10, + F11 ~func(A11) HKT_T11, + F12 ~func(A12) HKT_T12, + F13 ~func(A13) HKT_T13, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + A8, T8, + A9, T9, + A10, T10, + A11, T11, + A12, T12, + A13, T13, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T7_T8_T9_T10_T11_T12_T13, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T8_T9_T10_T11_T12_T13, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T9_T10_T11_T12_T13, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T10_T11_T12_T13, // HKT[func(T10) func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T11_T12_T13, // HKT[func(T11) func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T12_T13, // HKT[func(T12) func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_F_T13, // HKT[func(T13) T.Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] + HKT_TUPLE13 any, // HKT[Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + f8 F8, + f9 F9, + f10 F10, + f11 F11, + f12 F12, + f13 F13, + t T.Tuple13[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13], +) HKT_TUPLE13 { + return F.Pipe13( + f1(t.F1), + fmap(tupleConstructor13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + fap7(f8(t.F8)), + fap8(f9(t.F9)), + fap9(f10(t.F10)), + fap10(f11(t.F11)), + fap11(f12(t.F12)), + fap12(f13(t.F13)), + ) +} + +// tupleConstructor14 returns a curried version of [T.MakeTuple14] +func tupleConstructor14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { + return F.Curry14(T.MakeTuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) +} + +// SequenceT14 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 14 higher higher kinded types and returns a higher kinded type of a [Tuple14] with the resolved values. +func SequenceT14[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T7_T8_T9_T10_T11_T12_T13_T14, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T8_T9_T10_T11_T12_T13_T14, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13_T14) HKT_F_T9_T10_T11_T12_T13_T14, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13_T14) HKT_F_T10_T11_T12_T13_T14, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13_T14) HKT_F_T11_T12_T13_T14, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13_T14) HKT_F_T12_T13_T14, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13_T14) HKT_F_T13_T14, + AP12 ~func(HKT_T13) func(HKT_F_T13_T14) HKT_F_T14, + AP13 ~func(HKT_T14) func(HKT_F_T14) HKT_TUPLE14, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + T13, + T14, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_T14, // HKT[T14] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T9_T10_T11_T12_T13_T14, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T10_T11_T12_T13_T14, // HKT[func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T11_T12_T13_T14, // HKT[func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T12_T13_T14, // HKT[func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T13_T14, // HKT[func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T14, // HKT[func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_TUPLE14 any, // HKT[Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + fap13 AP13, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, + t8 HKT_T8, + t9 HKT_T9, + t10 HKT_T10, + t11 HKT_T11, + t12 HKT_T12, + t13 HKT_T13, + t14 HKT_T14, +) HKT_TUPLE14 { + return F.Pipe14( + t1, + fmap(tupleConstructor14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + fap7(t8), + fap8(t9), + fap9(t10), + fap10(t11), + fap11(t12), + fap12(t13), + fap13(t14), + ) +} + +// SequenceTuple14 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple14] of higher higher kinded types and returns a higher kinded type of a [Tuple14] with the resolved values. +func SequenceTuple14[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T7_T8_T9_T10_T11_T12_T13_T14, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T8_T9_T10_T11_T12_T13_T14, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13_T14) HKT_F_T9_T10_T11_T12_T13_T14, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13_T14) HKT_F_T10_T11_T12_T13_T14, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13_T14) HKT_F_T11_T12_T13_T14, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13_T14) HKT_F_T12_T13_T14, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13_T14) HKT_F_T13_T14, + AP12 ~func(HKT_T13) func(HKT_F_T13_T14) HKT_F_T14, + AP13 ~func(HKT_T14) func(HKT_F_T14) HKT_TUPLE14, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + T13, + T14, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_T14, // HKT[T14] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T9_T10_T11_T12_T13_T14, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T10_T11_T12_T13_T14, // HKT[func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T11_T12_T13_T14, // HKT[func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T12_T13_T14, // HKT[func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T13_T14, // HKT[func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T14, // HKT[func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_TUPLE14 any, // HKT[Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + fap13 AP13, + t T.Tuple14[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7, HKT_T8, HKT_T9, HKT_T10, HKT_T11, HKT_T12, HKT_T13, HKT_T14], +) HKT_TUPLE14 { + return F.Pipe14( + t.F1, + fmap(tupleConstructor14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + fap7(t.F8), + fap8(t.F9), + fap9(t.F10), + fap10(t.F11), + fap11(t.F12), + fap12(t.F13), + fap13(t.F14), + ) +} + +// TraverseTuple14 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple14] of base types and 14 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple14] with the resolved values. +func TraverseTuple14[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T7_T8_T9_T10_T11_T12_T13_T14, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13_T14) HKT_F_T8_T9_T10_T11_T12_T13_T14, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13_T14) HKT_F_T9_T10_T11_T12_T13_T14, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13_T14) HKT_F_T10_T11_T12_T13_T14, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13_T14) HKT_F_T11_T12_T13_T14, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13_T14) HKT_F_T12_T13_T14, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13_T14) HKT_F_T13_T14, + AP12 ~func(HKT_T13) func(HKT_F_T13_T14) HKT_F_T14, + AP13 ~func(HKT_T14) func(HKT_F_T14) HKT_TUPLE14, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + F8 ~func(A8) HKT_T8, + F9 ~func(A9) HKT_T9, + F10 ~func(A10) HKT_T10, + F11 ~func(A11) HKT_T11, + F12 ~func(A12) HKT_T12, + F13 ~func(A13) HKT_T13, + F14 ~func(A14) HKT_T14, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + A8, T8, + A9, T9, + A10, T10, + A11, T11, + A12, T12, + A13, T13, + A14, T14, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_T14, // HKT[T14] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T7_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T8_T9_T10_T11_T12_T13_T14, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T9_T10_T11_T12_T13_T14, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T10_T11_T12_T13_T14, // HKT[func(T10) func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T11_T12_T13_T14, // HKT[func(T11) func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T12_T13_T14, // HKT[func(T12) func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T13_T14, // HKT[func(T13) func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_F_T14, // HKT[func(T14) T.Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] + HKT_TUPLE14 any, // HKT[Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + fap13 AP13, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + f8 F8, + f9 F9, + f10 F10, + f11 F11, + f12 F12, + f13 F13, + f14 F14, + t T.Tuple14[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14], +) HKT_TUPLE14 { + return F.Pipe14( + f1(t.F1), + fmap(tupleConstructor14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + fap7(f8(t.F8)), + fap8(f9(t.F9)), + fap9(f10(t.F10)), + fap10(f11(t.F11)), + fap11(f12(t.F12)), + fap12(f13(t.F13)), + fap13(f14(t.F14)), + ) +} + +// tupleConstructor15 returns a curried version of [T.MakeTuple15] +func tupleConstructor15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any]() func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { + return F.Curry15(T.MakeTuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) +} + +// SequenceT15 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes 15 higher higher kinded types and returns a higher kinded type of a [Tuple15] with the resolved values. +func SequenceT15[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T8_T9_T10_T11_T12_T13_T14_T15, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T9_T10_T11_T12_T13_T14_T15, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13_T14_T15) HKT_F_T10_T11_T12_T13_T14_T15, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13_T14_T15) HKT_F_T11_T12_T13_T14_T15, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13_T14_T15) HKT_F_T12_T13_T14_T15, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13_T14_T15) HKT_F_T13_T14_T15, + AP12 ~func(HKT_T13) func(HKT_F_T13_T14_T15) HKT_F_T14_T15, + AP13 ~func(HKT_T14) func(HKT_F_T14_T15) HKT_F_T15, + AP14 ~func(HKT_T15) func(HKT_F_T15) HKT_TUPLE15, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + T13, + T14, + T15, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_T14, // HKT[T14] + HKT_T15, // HKT[T15] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T10_T11_T12_T13_T14_T15, // HKT[func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T11_T12_T13_T14_T15, // HKT[func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T12_T13_T14_T15, // HKT[func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T13_T14_T15, // HKT[func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T14_T15, // HKT[func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T15, // HKT[func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_TUPLE15 any, // HKT[Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + fap13 AP13, + fap14 AP14, + t1 HKT_T1, + t2 HKT_T2, + t3 HKT_T3, + t4 HKT_T4, + t5 HKT_T5, + t6 HKT_T6, + t7 HKT_T7, + t8 HKT_T8, + t9 HKT_T9, + t10 HKT_T10, + t11 HKT_T11, + t12 HKT_T12, + t13 HKT_T13, + t14 HKT_T14, + t15 HKT_T15, +) HKT_TUPLE15 { + return F.Pipe15( + t1, + fmap(tupleConstructor15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]()), + fap1(t2), + fap2(t3), + fap3(t4), + fap4(t5), + fap5(t6), + fap6(t7), + fap7(t8), + fap8(t9), + fap9(t10), + fap10(t11), + fap11(t12), + fap12(t13), + fap13(t14), + fap14(t15), + ) +} + +// SequenceTuple15 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple15] of higher higher kinded types and returns a higher kinded type of a [Tuple15] with the resolved values. +func SequenceTuple15[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T8_T9_T10_T11_T12_T13_T14_T15, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T9_T10_T11_T12_T13_T14_T15, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13_T14_T15) HKT_F_T10_T11_T12_T13_T14_T15, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13_T14_T15) HKT_F_T11_T12_T13_T14_T15, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13_T14_T15) HKT_F_T12_T13_T14_T15, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13_T14_T15) HKT_F_T13_T14_T15, + AP12 ~func(HKT_T13) func(HKT_F_T13_T14_T15) HKT_F_T14_T15, + AP13 ~func(HKT_T14) func(HKT_F_T14_T15) HKT_F_T15, + AP14 ~func(HKT_T15) func(HKT_F_T15) HKT_TUPLE15, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + T13, + T14, + T15, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_T14, // HKT[T14] + HKT_T15, // HKT[T15] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T10_T11_T12_T13_T14_T15, // HKT[func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T11_T12_T13_T14_T15, // HKT[func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T12_T13_T14_T15, // HKT[func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T13_T14_T15, // HKT[func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T14_T15, // HKT[func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T15, // HKT[func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_TUPLE15 any, // HKT[Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + fap13 AP13, + fap14 AP14, + t T.Tuple15[HKT_T1, HKT_T2, HKT_T3, HKT_T4, HKT_T5, HKT_T6, HKT_T7, HKT_T8, HKT_T9, HKT_T10, HKT_T11, HKT_T12, HKT_T13, HKT_T14, HKT_T15], +) HKT_TUPLE15 { + return F.Pipe15( + t.F1, + fmap(tupleConstructor15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]()), + fap1(t.F2), + fap2(t.F3), + fap3(t.F4), + fap4(t.F5), + fap5(t.F6), + fap6(t.F7), + fap7(t.F8), + fap8(t.F9), + fap9(t.F10), + fap10(t.F11), + fap11(t.F12), + fap12(t.F13), + fap13(t.F14), + fap14(t.F15), + ) +} + +// TraverseTuple15 is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Tuple15] of base types and 15 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Tuple15] with the resolved values. +func TraverseTuple15[ + MAP ~func(func(T1) func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) func(HKT_T1) HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP1 ~func(HKT_T2) func(HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP2 ~func(HKT_T3) func(HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP3 ~func(HKT_T4) func(HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP4 ~func(HKT_T5) func(HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP5 ~func(HKT_T6) func(HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15, + AP6 ~func(HKT_T7) func(HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T8_T9_T10_T11_T12_T13_T14_T15, + AP7 ~func(HKT_T8) func(HKT_F_T8_T9_T10_T11_T12_T13_T14_T15) HKT_F_T9_T10_T11_T12_T13_T14_T15, + AP8 ~func(HKT_T9) func(HKT_F_T9_T10_T11_T12_T13_T14_T15) HKT_F_T10_T11_T12_T13_T14_T15, + AP9 ~func(HKT_T10) func(HKT_F_T10_T11_T12_T13_T14_T15) HKT_F_T11_T12_T13_T14_T15, + AP10 ~func(HKT_T11) func(HKT_F_T11_T12_T13_T14_T15) HKT_F_T12_T13_T14_T15, + AP11 ~func(HKT_T12) func(HKT_F_T12_T13_T14_T15) HKT_F_T13_T14_T15, + AP12 ~func(HKT_T13) func(HKT_F_T13_T14_T15) HKT_F_T14_T15, + AP13 ~func(HKT_T14) func(HKT_F_T14_T15) HKT_F_T15, + AP14 ~func(HKT_T15) func(HKT_F_T15) HKT_TUPLE15, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + F3 ~func(A3) HKT_T3, + F4 ~func(A4) HKT_T4, + F5 ~func(A5) HKT_T5, + F6 ~func(A6) HKT_T6, + F7 ~func(A7) HKT_T7, + F8 ~func(A8) HKT_T8, + F9 ~func(A9) HKT_T9, + F10 ~func(A10) HKT_T10, + F11 ~func(A11) HKT_T11, + F12 ~func(A12) HKT_T12, + F13 ~func(A13) HKT_T13, + F14 ~func(A14) HKT_T14, + F15 ~func(A15) HKT_T15, + A1, T1, + A2, T2, + A3, T3, + A4, T4, + A5, T5, + A6, T6, + A7, T7, + A8, T8, + A9, T9, + A10, T10, + A11, T11, + A12, T12, + A13, T13, + A14, T14, + A15, T15, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_T3, // HKT[T3] + HKT_T4, // HKT[T4] + HKT_T5, // HKT[T5] + HKT_T6, // HKT[T6] + HKT_T7, // HKT[T7] + HKT_T8, // HKT[T8] + HKT_T9, // HKT[T9] + HKT_T10, // HKT[T10] + HKT_T11, // HKT[T11] + HKT_T12, // HKT[T12] + HKT_T13, // HKT[T13] + HKT_T14, // HKT[T14] + HKT_T15, // HKT[T15] + HKT_F_T2_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T3_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T4_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T5_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T6_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T6) func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T7_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T7) func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T8_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T8) func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T9_T10_T11_T12_T13_T14_T15, // HKT[func(T9) func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T10_T11_T12_T13_T14_T15, // HKT[func(T10) func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T11_T12_T13_T14_T15, // HKT[func(T11) func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T12_T13_T14_T15, // HKT[func(T12) func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T13_T14_T15, // HKT[func(T13) func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T14_T15, // HKT[func(T14) func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_F_T15, // HKT[func(T15) T.Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] + HKT_TUPLE15 any, // HKT[Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] +]( + fmap MAP, + fap1 AP1, + fap2 AP2, + fap3 AP3, + fap4 AP4, + fap5 AP5, + fap6 AP6, + fap7 AP7, + fap8 AP8, + fap9 AP9, + fap10 AP10, + fap11 AP11, + fap12 AP12, + fap13 AP13, + fap14 AP14, + f1 F1, + f2 F2, + f3 F3, + f4 F4, + f5 F5, + f6 F6, + f7 F7, + f8 F8, + f9 F9, + f10 F10, + f11 F11, + f12 F12, + f13 F13, + f14 F14, + f15 F15, + t T.Tuple15[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15], +) HKT_TUPLE15 { + return F.Pipe15( + f1(t.F1), + fmap(tupleConstructor15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]()), + fap1(f2(t.F2)), + fap2(f3(t.F3)), + fap3(f4(t.F4)), + fap4(f5(t.F5)), + fap5(f6(t.F6)), + fap6(f7(t.F7)), + fap7(f8(t.F8)), + fap8(f9(t.F9)), + fap9(f10(t.F10)), + fap10(f11(t.F11)), + fap11(f12(t.F12)), + fap12(f13(t.F13)), + fap13(f14(t.F14)), + fap14(f15(t.F15)), + ) +} diff --git a/v2/internal/apply/testing/laws.go b/v2/internal/apply/testing/laws.go new file mode 100644 index 0000000..5a36bf3 --- /dev/null +++ b/v2/internal/apply/testing/laws.go @@ -0,0 +1,180 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/eq" + "github.com/IBM/fp-go/v2/internal/apply" + "github.com/IBM/fp-go/v2/internal/functor" + FCT "github.com/IBM/fp-go/v2/internal/functor/testing" + "github.com/IBM/fp-go/v2/internal/pointed" + "github.com/stretchr/testify/assert" +) + +// Apply associative composition law +// +// F.ap(F.ap(F.map(fbc, bc => ab => a => bc(ab(a))), fab), fa) <-> F.ap(fbc, F.ap(fab, fa)) +// +// Deprecated: use [ApplyAssertAssociativeComposition] instead +func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, + eq E.Eq[HKTC], + + fofab func(func(A) B) HKTAB, + fofbc func(func(B) C) HKTBC, + + fmap func(HKTBC, func(func(B) C) func(func(A) B) func(A) C) HKTABAC, + + fapab func(HKTAB, HKTA) HKTB, + fapbc func(HKTBC, HKTB) HKTC, + fapac func(HKTAC, HKTA) HKTC, + + fapabac func(HKTABAC, HKTAB) HKTAC, + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + t.Helper() + return func(fa HKTA) bool { + + fab := fofab(ab) + fbc := fofbc(bc) + + left := fapac(fapabac(fmap(fbc, func(bc func(B) C) func(func(A) B) func(A) C { + return func(ab func(A) B) func(A) C { + return func(a A) C { + return bc(ab(a)) + } + } + }), fab), fa) + + right := fapbc(fbc, fapab(fab, fa)) + + return assert.True(t, eq.Equals(left, right), "Apply associative composition") + } +} + +// Apply associative composition law +// +// F.ap(F.ap(F.map(fbc, bc => ab => a => bc(ab(a))), fab), fa) <-> F.ap(fbc, F.ap(fab, fa)) +func ApplyAssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, + eq E.Eq[HKTC], + + fofab pointed.Pointed[func(A) B, HKTAB], + fofbc pointed.Pointed[func(B) C, HKTBC], + + fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC], + + fapab apply.Apply[A, B, HKTA, HKTB, HKTAB], + fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC], + fapac apply.Apply[A, C, HKTA, HKTC, HKTAC], + + fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC], + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + t.Helper() + return func(fa HKTA) bool { + + fab := fofab.Of(ab) + fbc := fofbc.Of(bc) + + left := fapac.Ap(fa)(fapabac.Ap(fab)(fmap.Map(func(bc func(B) C) func(func(A) B) func(A) C { + return func(ab func(A) B) func(A) C { + return func(a A) C { + return bc(ab(a)) + } + } + })(fbc))) + + right := fapbc.Ap(fapab.Ap(fa)(fab))(fbc) + + return assert.True(t, eq.Equals(left, right), "Apply associative composition") + } +} + +// AssertLaws asserts the apply laws `identity`, `composition` and `associative composition` +// +// Deprecated: use [ApplyAssertLaws] instead +func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqc E.Eq[HKTC], + + fofab func(func(A) B) HKTAB, + fofbc func(func(B) C) HKTBC, + + faa func(HKTA, func(A) A) HKTA, + fab func(HKTA, func(A) B) HKTB, + fac func(HKTA, func(A) C) HKTC, + fbc func(HKTB, func(B) C) HKTC, + + fmap func(HKTBC, func(func(B) C) func(func(A) B) func(A) C) HKTABAC, + + fapab func(HKTAB, HKTA) HKTB, + fapbc func(HKTBC, HKTB) HKTC, + fapac func(HKTAC, HKTA) HKTC, + + fapabac func(HKTABAC, HKTAB) HKTAC, + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + // mark as test helper + t.Helper() + // functor laws + functor := FCT.AssertLaws(t, eqa, eqc, faa, fab, fac, fbc, ab, bc) + // associative composition laws + composition := AssertAssociativeComposition(t, eqc, fofab, fofbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc) + + return func(fa HKTA) bool { + return functor(fa) && composition(fa) + } +} + +// ApplyAssertLaws asserts the apply laws `identity`, `composition` and `associative composition` +func ApplyAssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqc E.Eq[HKTC], + + fofab pointed.Pointed[func(A) B, HKTAB], + fofbc pointed.Pointed[func(B) C, HKTBC], + + faa functor.Functor[A, A, HKTA, HKTA], + + fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC], + + fapab apply.Apply[A, B, HKTA, HKTB, HKTAB], + fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC], + fapac apply.Apply[A, C, HKTA, HKTC, HKTAC], + + fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC], + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + // mark as test helper + t.Helper() + // functor laws + functor := FCT.FunctorAssertLaws(t, eqa, eqc, faa, apply.ToFunctor(fapab), apply.ToFunctor(fapac), apply.ToFunctor(fapbc), ab, bc) + // associative composition laws + composition := ApplyAssertAssociativeComposition(t, eqc, fofab, fofbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc) + + return func(fa HKTA) bool { + return functor(fa) && composition(fa) + } +} diff --git a/v2/internal/apply/types.go b/v2/internal/apply/types.go new file mode 100644 index 0000000..10d31d1 --- /dev/null +++ b/v2/internal/apply/types.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apply + +import ( + "github.com/IBM/fp-go/v2/internal/functor" +) + +type Apply[A, B, HKTA, HKTB, HKTFAB any] interface { + functor.Functor[A, B, HKTA, HKTB] + Ap(HKTA) func(HKTFAB) HKTB +} + +// ToFunctor converts from [Apply] to [functor.Functor] +func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Apply[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] { + return ap +} diff --git a/v2/internal/array/array.go b/v2/internal/array/array.go new file mode 100644 index 0000000..3cd1f6c --- /dev/null +++ b/v2/internal/array/array.go @@ -0,0 +1,126 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +func Slice[GA ~[]A, A any](low, high int) func(as GA) GA { + return func(as GA) GA { + return as[low:high] + } +} + +func IsEmpty[GA ~[]A, A any](as GA) bool { + return len(as) == 0 +} + +func IsNil[GA ~[]A, A any](as GA) bool { + return as == nil +} + +func IsNonNil[GA ~[]A, A any](as GA) bool { + return as != nil +} + +func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B { + current := initial + count := len(fa) + for i := 0; i < count; i++ { + current = f(current, fa[i]) + } + return current +} + +func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B { + current := initial + count := len(fa) + for i := 0; i < count; i++ { + current = f(i, current, fa[i]) + } + return current +} + +func ReduceRight[GA ~[]A, A, B any](fa GA, f func(A, B) B, initial B) B { + current := initial + count := len(fa) + for i := count - 1; i >= 0; i-- { + current = f(fa[i], current) + } + return current +} + +func ReduceRightWithIndex[GA ~[]A, A, B any](fa GA, f func(int, A, B) B, initial B) B { + current := initial + count := len(fa) + for i := count - 1; i >= 0; i-- { + current = f(i, fa[i], current) + } + return current +} + +func Append[GA ~[]A, A any](as GA, a A) GA { + return append(as, a) +} + +func Push[GA ~[]A, A any](as GA, a A) GA { + l := len(as) + cpy := make(GA, l+1) + copy(cpy, as) + cpy[l] = a + return cpy +} + +func Empty[GA ~[]A, A any]() GA { + return make(GA, 0) +} + +func upsertAt[GA ~[]A, A any](fa GA, a A) GA { + buf := make(GA, len(fa)+1) + buf[copy(buf, fa)] = a + return buf +} + +func UpsertAt[GA ~[]A, A any](a A) func(GA) GA { + return func(ma GA) GA { + return upsertAt(ma, a) + } +} + +func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB { + count := len(as) + bs := make(GB, count) + for i := count - 1; i >= 0; i-- { + bs[i] = f(as[i]) + } + return bs +} + +func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB { + return func(as GA) GB { + return MonadMap[GA, GB](as, f) + } +} + +func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B) GB { + count := len(as) + bs := make(GB, count) + for i := count - 1; i >= 0; i-- { + bs[i] = f(i, as[i]) + } + return bs +} + +func ConstNil[GA ~[]A, A any]() GA { + return (GA)(nil) +} diff --git a/v2/internal/array/prepend.go b/v2/internal/array/prepend.go new file mode 100644 index 0000000..437a5d7 --- /dev/null +++ b/v2/internal/array/prepend.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +// Prepend prepends a single value to an array +func Prepend[ENDO ~func(AS) AS, AS ~[]A, A any](head A) ENDO { + return func(as AS) AS { + l := len(as) + cpy := make(AS, l+1) + copy(cpy[1:], as) + cpy[0] = head + return cpy + + } +} diff --git a/v2/internal/array/traverse.go b/v2/internal/array/traverse.go new file mode 100644 index 0000000..ec32373 --- /dev/null +++ b/v2/internal/array/traverse.go @@ -0,0 +1,152 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +/* +* +We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces + +HKTRB = HKT +HKTB = HKT +HKTAB = HKT +*/ +func MonadTraverse[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + ta GA, + f func(A) HKTB) HKTRB { + return MonadTraverseReduce(fof, fmap, fap, ta, f, Append[GB, B], Empty[GB]()) +} + +/* +* +We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces + +HKTRB = HKT +HKTB = HKT +HKTAB = HKT +*/ +func MonadTraverseWithIndex[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + ta GA, + f func(int, A) HKTB) HKTRB { + return MonadTraverseReduceWithIndex(fof, fmap, fap, ta, f, Append[GB, B], Empty[GB]()) +} + +func Traverse[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + f func(A) HKTB) func(GA) HKTRB { + + return func(ma GA) HKTRB { + return MonadTraverse(fof, fmap, fap, ma, f) + } +} + +func TraverseWithIndex[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + f func(int, A) HKTB) func(GA) HKTRB { + + return func(ma GA) HKTRB { + return MonadTraverseWithIndex(fof, fmap, fap, ma, f) + } +} + +func MonadTraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + ta GA, + + transform func(A) HKTB, + reduce func(GB, B) GB, + initial GB, +) HKTRB { + mmap := fmap(F.Curry2(reduce)) + + return Reduce(ta, func(r HKTRB, a A) HKTRB { + return F.Pipe2( + r, + mmap, + fap(transform(a)), + ) + }, fof(initial)) +} + +func MonadTraverseReduceWithIndex[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + ta GA, + + transform func(int, A) HKTB, + reduce func(GB, B) GB, + initial GB, +) HKTRB { + mmap := fmap(F.Curry2(reduce)) + + return ReduceWithIndex(ta, func(idx int, r HKTRB, a A) HKTRB { + return F.Pipe2( + r, + mmap, + fap(transform(idx, a)), + ) + }, fof(initial)) +} + +func TraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + transform func(A) HKTB, + reduce func(GB, B) GB, + initial GB, +) func(GA) HKTRB { + return func(ta GA) HKTRB { + return MonadTraverseReduce(fof, fmap, fap, ta, transform, reduce, initial) + } +} + +func TraverseReduceWithIndex[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + transform func(int, A) HKTB, + reduce func(GB, B) GB, + initial GB, +) func(GA) HKTRB { + return func(ta GA) HKTRB { + return MonadTraverseReduceWithIndex(fof, fmap, fap, ta, transform, reduce, initial) + } +} diff --git a/v2/internal/bracket/bracket.go b/v2/internal/bracket/bracket.go new file mode 100644 index 0000000..61e03dd --- /dev/null +++ b/v2/internal/bracket/bracket.go @@ -0,0 +1,52 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[ + GA, // IOEither[E, A] + GB, // IOEither[E, A] + GANY, // IOEither[E, ANY] + + EB, // Either[E, B] + + A, B, ANY any]( + + ofeb func(EB) GB, + + chainab func(GA, func(A) GB) GB, + chainebb func(GB, func(EB) GB) GB, + chainany func(GANY, func(ANY) GB) GB, + + acquire GA, + use func(A) GB, + release func(A, EB) GANY, +) GB { + return chainab(acquire, + func(a A) GB { + return chainebb(use(a), func(eb EB) GB { + return chainany( + release(a, eb), + F.Constant1[ANY](ofeb(eb)), + ) + }) + }) +} diff --git a/v2/internal/chain/chain.go b/v2/internal/chain/chain.go new file mode 100644 index 0000000..f2c0cf2 --- /dev/null +++ b/v2/internal/chain/chain.go @@ -0,0 +1,110 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package chain + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// HKTA=HKT[A] +// HKTB=HKT[B] +func MonadChainFirst[A, B, HKTA, HKTB any]( + mchain func(HKTA, func(A) HKTA) HKTA, + mmap func(HKTB, func(B) A) HKTA, + first HKTA, + f func(A) HKTB, +) HKTA { + return mchain(first, func(a A) HKTA { + return mmap(f(a), F.Constant1[B](a)) + }) +} + +func MonadChain[A, B, HKTA, HKTB any]( + mchain func(HKTA, func(A) HKTB) HKTB, + first HKTA, + f func(A) HKTB, +) HKTB { + return mchain(first, f) +} + +// HKTA=HKT[A] +// HKTB=HKT[B] +func ChainFirst[A, B, HKTA, HKTB any]( + mchain func(func(A) HKTA) func(HKTA) HKTA, + mmap func(func(B) A) func(HKTB) HKTA, + f func(A) HKTB) func(HKTA) HKTA { + return mchain(func(a A) HKTA { + return mmap(F.Constant1[B](a))(f(a)) + }) +} + +func Chain[A, B, HKTA, HKTB any]( + mchain func(func(A) HKTB) func(HKTA) HKTB, + f func(A) HKTB, +) func(HKTA) HKTB { + return mchain(f) +} + +func MonadBind[S1, S2, B, HKTS1, HKTS2, HKTB any]( + mchain func(HKTS1, func(S1) HKTS2) HKTS2, + mmap func(HKTB, func(B) S2) HKTS2, + first HKTS1, + key func(B) func(S1) S2, + f func(S1) HKTB, +) HKTS2 { + return mchain(first, func(s1 S1) HKTS2 { + return mmap(f(s1), func(b B) S2 { + return key(b)(s1) + }) + }) +} + +func Bind[S1, S2, B, HKTS1, HKTS2, HKTB any]( + mchain func(func(S1) HKTS2) func(HKTS1) HKTS2, + mmap func(func(B) S2) func(HKTB) HKTS2, + key func(B) func(S1) S2, + f func(S1) HKTB, +) func(HKTS1) HKTS2 { + mapb := F.Flow2( + F.Flip(key), + mmap, + ) + return mchain(func(s1 S1) HKTS2 { + return F.Pipe2( + s1, + f, + F.Pipe1( + s1, + mapb, + ), + ) + }) +} + +func BindTo[S1, B, HKTS1, HKTB any]( + mmap func(func(B) S1) func(HKTB) HKTS1, + key func(B) S1, +) func(fa HKTB) HKTS1 { + return mmap(key) +} + +func MonadBindTo[S1, B, HKTS1, HKTB any]( + mmap func(HKTB, func(B) S1) HKTS1, + first HKTB, + key func(B) S1, +) HKTS1 { + return mmap(first, key) +} diff --git a/v2/internal/chain/testing/laws.go b/v2/internal/chain/testing/laws.go new file mode 100644 index 0000000..6657533 --- /dev/null +++ b/v2/internal/chain/testing/laws.go @@ -0,0 +1,170 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/apply" + L "github.com/IBM/fp-go/v2/internal/apply/testing" + "github.com/IBM/fp-go/v2/internal/chain" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/pointed" + "github.com/stretchr/testify/assert" +) + +// Chain associativity law +// +// F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc)) +// +// Deprecated: use [ChainAssertAssociativity] instead +func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T, + eq E.Eq[HKTC], + + fofb func(B) HKTB, + fofc func(C) HKTC, + + chainab func(HKTA, func(A) HKTB) HKTB, + chainac func(HKTA, func(A) HKTC) HKTC, + chainbc func(HKTB, func(B) HKTC) HKTC, + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + return func(fa HKTA) bool { + + afb := F.Flow2(ab, fofb) + bfc := F.Flow2(bc, fofc) + + left := chainbc(chainab(fa, afb), bfc) + + right := chainac(fa, func(a A) HKTC { + return chainbc(afb(a), bfc) + }) + + return assert.True(t, eq.Equals(left, right), "Chain associativity") + } +} + +// Chain associativity law +// +// F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc)) +func ChainAssertAssociativity[HKTA, HKTB, HKTC, HKTAB, HKTAC, HKTBC, A, B, C any](t *testing.T, + eq E.Eq[HKTC], + + fofb pointed.Pointed[B, HKTB], + fofc pointed.Pointed[C, HKTC], + + chainab chain.Chainable[A, B, HKTA, HKTB, HKTAB], + chainac chain.Chainable[A, C, HKTA, HKTC, HKTAC], + chainbc chain.Chainable[B, C, HKTB, HKTC, HKTBC], + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + return func(fa HKTA) bool { + + afb := F.Flow2(ab, fofb.Of) + bfc := F.Flow2(bc, fofc.Of) + + left := chainbc.Chain(bfc)(chainab.Chain(afb)(fa)) + + right := chainac.Chain(func(a A) HKTC { + return chainbc.Chain(bfc)(afb(a)) + })(fa) + + return assert.True(t, eq.Equals(left, right), "Chain associativity") + } +} + +// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition` and `associativity` +// +// Deprecated: use [ChainAssertLaws] instead +func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqc E.Eq[HKTC], + + fofb func(B) HKTB, + fofc func(C) HKTC, + + fofab func(func(A) B) HKTAB, + fofbc func(func(B) C) HKTBC, + + faa func(HKTA, func(A) A) HKTA, + fab func(HKTA, func(A) B) HKTB, + fac func(HKTA, func(A) C) HKTC, + fbc func(HKTB, func(B) C) HKTC, + + fmap func(HKTBC, func(func(B) C) func(func(A) B) func(A) C) HKTABAC, + + chainab func(HKTA, func(A) HKTB) HKTB, + chainac func(HKTA, func(A) HKTC) HKTC, + chainbc func(HKTB, func(B) HKTC) HKTC, + + fapab func(HKTAB, HKTA) HKTB, + fapbc func(HKTBC, HKTB) HKTC, + fapac func(HKTAC, HKTA) HKTC, + + fapabac func(HKTABAC, HKTAB) HKTAC, + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + // apply laws + apply := L.AssertLaws(t, eqa, eqc, fofab, fofbc, faa, fab, fac, fbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc) + // chain laws + associativity := AssertAssociativity(t, eqc, fofb, fofc, chainab, chainac, chainbc, ab, bc) + + return func(fa HKTA) bool { + return apply(fa) && associativity(fa) + } +} + +// ChainAssertLaws asserts the apply laws `identity`, `composition`, `associative composition` and `associativity` +func ChainAssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqc E.Eq[HKTC], + + fofb pointed.Pointed[B, HKTB], + fofc pointed.Pointed[C, HKTC], + + fofab pointed.Pointed[func(A) B, HKTAB], + fofbc pointed.Pointed[func(B) C, HKTBC], + + faa functor.Functor[A, A, HKTA, HKTA], + + fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC], + + chainab chain.Chainable[A, B, HKTA, HKTB, HKTAB], + chainac chain.Chainable[A, C, HKTA, HKTC, HKTAC], + chainbc chain.Chainable[B, C, HKTB, HKTC, HKTBC], + + fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC], + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + // apply laws + apply := L.ApplyAssertLaws(t, eqa, eqc, fofab, fofbc, faa, fmap, chain.ToApply(chainab), chain.ToApply(chainbc), chain.ToApply(chainac), fapabac, ab, bc) + // chain laws + associativity := ChainAssertAssociativity(t, eqc, fofb, fofc, chainab, chainac, chainbc, ab, bc) + + return func(fa HKTA) bool { + return apply(fa) && associativity(fa) + } +} diff --git a/v2/internal/chain/types.go b/v2/internal/chain/types.go new file mode 100644 index 0000000..e862502 --- /dev/null +++ b/v2/internal/chain/types.go @@ -0,0 +1,36 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package chain + +import ( + "github.com/IBM/fp-go/v2/internal/apply" + "github.com/IBM/fp-go/v2/internal/functor" +) + +type Chainable[A, B, HKTA, HKTB, HKTFAB any] interface { + apply.Apply[A, B, HKTA, HKTB, HKTFAB] + Chain(func(A) HKTB) func(HKTA) HKTB +} + +// ToFunctor converts from [Chainable] to [functor.Functor] +func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Chainable[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] { + return ap +} + +// ToApply converts from [Chainable] to [functor.Functor] +func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Chainable[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] { + return ap +} diff --git a/v2/internal/eithert/either.go b/v2/internal/eithert/either.go new file mode 100644 index 0000000..0b06c07 --- /dev/null +++ b/v2/internal/eithert/either.go @@ -0,0 +1,165 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eithert + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/apply" + FC "github.com/IBM/fp-go/v2/internal/functor" +) + +func MonadAlt[LAZY ~func() HKTFA, E, A, HKTFA any]( + fof func(ET.Either[E, A]) HKTFA, + fchain func(HKTFA, func(ET.Either[E, A]) HKTFA) HKTFA, + + first HKTFA, + second LAZY) HKTFA { + + return fchain(first, ET.Fold(F.Ignore1of1[E](second), F.Flow2(ET.Of[E, A], fof))) +} + +func Alt[LAZY ~func() HKTFA, E, A, HKTFA any]( + fof func(ET.Either[E, A]) HKTFA, + fchain func(HKTFA, func(ET.Either[E, A]) HKTFA) HKTFA, + + second LAZY) func(HKTFA) HKTFA { + + return func(fa HKTFA) HKTFA { + return MonadAlt(fof, fchain, fa, second) + } +} + +// HKTFA = HKT> +// HKTFB = HKT> +func MonadMap[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[E, B]) HKTFB, fa HKTFA, f func(A) B) HKTFB { + // HKTGA = Either[E, A] + // HKTGB = Either[E, B] + return FC.MonadMap(fmap, ET.MonadMap[E, A, B], fa, f) +} + +func Map[E, A, B, HKTFA, HKTFB any]( + fmap func(func(ET.Either[E, A]) ET.Either[E, B]) func(HKTFA) HKTFB, + f func(A) B) func(HKTFA) HKTFB { + // HKTGA = Either[E, A] + // HKTGB = Either[E, B] + return FC.Map(fmap, ET.Map[E, A, B], f) +} + +// HKTFA = HKT> +// HKTFB = HKT> +func MonadBiMap[E1, E2, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E1, A]) ET.Either[E2, B]) HKTFB, fa HKTFA, f func(E1) E2, g func(A) B) HKTFB { + // HKTGA = Either[E, A] + // HKTGB = Either[E, B] + return fmap(fa, ET.BiMap(f, g)) +} + +// HKTFA = HKT> +// HKTFB = HKT> +func BiMap[E1, E2, A, B, HKTFA, HKTFB any]( + fmap func(func(ET.Either[E1, A]) ET.Either[E2, B]) func(HKTFA) HKTFB, + f func(E1) E2, g func(A) B) func(HKTFA) HKTFB { + // HKTGA = Either[E, A] + // HKTGB = Either[E, B] + return fmap(ET.BiMap(f, g)) +} + +// HKTFA = HKT> +// HKTFB = HKT> +func MonadChain[E, A, B, HKTFA, HKTFB any]( + fchain func(HKTFA, func(ET.Either[E, A]) HKTFB) HKTFB, + fof func(ET.Either[E, B]) HKTFB, + ma HKTFA, + f func(A) HKTFB) HKTFB { + // dispatch to the even more generic implementation + return fchain(ma, ET.Fold(F.Flow2(ET.Left[B, E], fof), f)) +} + +func Chain[E, A, B, HKTFA, HKTFB any]( + fchain func(func(ET.Either[E, A]) HKTFB) func(HKTFA) HKTFB, + fof func(ET.Either[E, B]) HKTFB, + f func(A) HKTFB) func(HKTFA) HKTFB { + // dispatch to the even more generic implementation + return fchain(ET.Fold(F.Flow2(ET.Left[B, E], fof), f)) +} + +func MonadAp[E, A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any]( + fap func(HKTFGAB, HKTFA) HKTFB, + fmap func(HKTFAB, func(ET.Either[E, func(A) B]) func(ET.Either[E, A]) ET.Either[E, B]) HKTFGAB, + fab HKTFAB, + fa HKTFA) HKTFB { + return apply.MonadAp(fap, fmap, ET.MonadAp[B, E, A], fab, fa) +} + +func Ap[E, A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any]( + fap func(HKTFA) func(HKTFGAB) HKTFB, + fmap func(func(ET.Either[E, func(A) B]) func(ET.Either[E, A]) ET.Either[E, B]) func(HKTFAB) HKTFGAB, + fa HKTFA) func(HKTFAB) HKTFB { + return apply.Ap(fap, fmap, ET.Ap[B, E, A], fa) +} + +func Right[E, A, HKTA any](fof func(ET.Either[E, A]) HKTA, a A) HKTA { + return F.Pipe2(a, ET.Right[E, A], fof) +} + +func Left[E, A, HKTA any](fof func(ET.Either[E, A]) HKTA, e E) HKTA { + return F.Pipe2(e, ET.Left[A, E], fof) +} + +// HKTA = HKT[A] +// HKTEA = HKT[Either[E, A]] +func RightF[E, A, HKTA, HKTEA any](fmap func(HKTA, func(A) ET.Either[E, A]) HKTEA, fa HKTA) HKTEA { + return fmap(fa, ET.Right[E, A]) +} + +// HKTE = HKT[E] +// HKTEA = HKT[Either[E, A]] +func LeftF[E, A, HKTE, HKTEA any](fmap func(HKTE, func(E) ET.Either[E, A]) HKTEA, fe HKTE) HKTEA { + return fmap(fe, ET.Left[A, E]) +} + +func FoldE[E, A, HKTEA, HKTB any](mchain func(HKTEA, func(ET.Either[E, A]) HKTB) HKTB, ma HKTEA, onLeft func(E) HKTB, onRight func(A) HKTB) HKTB { + return mchain(ma, ET.Fold(onLeft, onRight)) +} + +func MatchE[E, A, HKTEA, HKTB any](mchain func(HKTEA, func(ET.Either[E, A]) HKTB) HKTB, onLeft func(E) HKTB, onRight func(A) HKTB) func(HKTEA) HKTB { + return F.Bind2nd(mchain, ET.Fold(onLeft, onRight)) +} + +func GetOrElse[E, A, HKTEA, HKTA any](mchain func(HKTEA, func(ET.Either[E, A]) HKTA) HKTA, mof func(A) HKTA, onLeft func(E) HKTA) func(HKTEA) HKTA { + return MatchE(mchain, onLeft, mof) +} + +func OrElse[E1, E2, A, HKTE1A, HKTE2A any](mchain func(HKTE1A, func(ET.Either[E1, A]) HKTE2A) HKTE2A, mof func(ET.Either[E2, A]) HKTE2A, onLeft func(E1) HKTE2A) func(HKTE1A) HKTE2A { + return MatchE(mchain, onLeft, F.Flow2(ET.Right[E2, A], mof)) +} + +func OrLeft[E1, E2, A, HKTE1A, HKTE2, HKTE2A any]( + mchain func(HKTE1A, func(ET.Either[E1, A]) HKTE2A) HKTE2A, + mmap func(HKTE2, func(E2) ET.Either[E2, A]) HKTE2A, + mof func(ET.Either[E2, A]) HKTE2A, + onLeft func(E1) HKTE2) func(HKTE1A) HKTE2A { + + return F.Bind2nd(mchain, ET.Fold(F.Flow2(onLeft, F.Bind2nd(mmap, ET.Left[A, E2])), F.Flow2(ET.Right[E2, A], mof))) +} + +func MonadMapLeft[E, A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(ET.Either[E, A]) ET.Either[B, A]) HKTFB, fa HKTFA, f func(E) B) HKTFB { + return FC.MonadMap(fmap, ET.MonadMapLeft[E, A, B], fa, f) +} + +func MapLeft[E, A, B, HKTFA, HKTFB any](fmap func(func(ET.Either[E, A]) ET.Either[B, A]) func(HKTFA) HKTFB, f func(E) B) func(HKTFA) HKTFB { + return FC.Map(fmap, ET.MapLeft[A, E, B], f) +} diff --git a/v2/internal/eq/eq.go b/v2/internal/eq/eq.go new file mode 100644 index 0000000..0e449e9 --- /dev/null +++ b/v2/internal/eq/eq.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eq + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" +) + +// Eq implements an equals predicate on the basis of `map` and `ap` +func Eq[HKTA, HKTABOOL, HKTBOOL, A any]( + fmap func(HKTA, func(A) func(A) bool) HKTABOOL, + fap func(HKTABOOL, HKTA) HKTBOOL, + + e EQ.Eq[A], +) func(l, r HKTA) HKTBOOL { + c := F.Curry2(e.Equals) + return func(fl, fr HKTA) HKTBOOL { + return fap(fmap(fl, c), fr) + } +} diff --git a/v2/internal/exec/exec.go b/v2/internal/exec/exec.go new file mode 100644 index 0000000..070653d --- /dev/null +++ b/v2/internal/exec/exec.go @@ -0,0 +1,46 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package exec + +import ( + "bytes" + "context" + "fmt" + "os/exec" + + EX "github.com/IBM/fp-go/v2/exec" + + P "github.com/IBM/fp-go/v2/pair" +) + +func Exec(ctx context.Context, name string, args []string, in []byte) (EX.CommandOutput, error) { + // command input + cmd := exec.CommandContext(ctx, name, args...) + cmd.Stdin = bytes.NewReader(in) + // command result + var stdOut bytes.Buffer + var stdErr bytes.Buffer + + cmd.Stdout = &stdOut + cmd.Stderr = &stdErr + // execute the command + err := cmd.Run() + if err != nil { + err = fmt.Errorf("command execution of [%s][%s] failed, stdout [%s], stderr [%s], cause [%w]", name, args, stdOut.String(), stdErr.String(), err) + } + // return the outputs + return P.MakePair(stdOut.Bytes(), stdErr.Bytes()), err +} diff --git a/v2/internal/file/file.go b/v2/internal/file/file.go new file mode 100644 index 0000000..1e7b861 --- /dev/null +++ b/v2/internal/file/file.go @@ -0,0 +1,52 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "bytes" + "context" + "io" + + E "github.com/IBM/fp-go/v2/either" +) + +type ( + readerWithContext struct { + ctx context.Context + delegate io.Reader + } +) + +func (rdr *readerWithContext) Read(p []byte) (int, error) { + // check for cancellarion + if err := rdr.ctx.Err(); err != nil { + return 0, err + } + // simply dispatch + return rdr.delegate.Read(p) +} + +// MakeReader creates a context aware reader +func MakeReader(ctx context.Context, rdr io.Reader) io.Reader { + return &readerWithContext{ctx, rdr} +} + +// ReadAll reads the content of a reader and allows it to be canceled +func ReadAll(ctx context.Context, rdr io.Reader) E.Either[error, []byte] { + var buffer bytes.Buffer + _, err := io.Copy(&buffer, MakeReader(ctx, rdr)) + return E.TryCatchError(buffer.Bytes(), err) +} diff --git a/v2/internal/file/resource.go b/v2/internal/file/resource.go new file mode 100644 index 0000000..f59ce81 --- /dev/null +++ b/v2/internal/file/resource.go @@ -0,0 +1,64 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[ + GA, + GR, + GANY, + E, R, A, ANY any]( + mchain func(GR, func(R) GA) GA, + mfold1 func(GA, func(E) GA, func(A) GA) GA, + mfold2 func(GANY, func(E) GA, func(ANY) GA) GA, + mmap func(GANY, func(ANY) A) GA, + left func(E) GA, +) func(onCreate func() GR, onRelease func(R) GANY) func(func(R) GA) GA { + + return func(onCreate func() GR, onRelease func(R) GANY) func(func(R) GA) GA { + + return func(f func(R) GA) GA { + return mchain( + onCreate(), func(r R) GA { + // handle errors + return mfold1( + f(r), + func(e E) GA { + // the original error + err := left(e) + // if resource processing produced and error, still release the resource but return the first error + return mfold2( + onRelease(r), + F.Constant1[E](err), + F.Constant1[ANY](err), + ) + }, + func(a A) GA { + // if resource processing succeeded, release the resource. If this fails return failure, else the original error + return F.Pipe1( + onRelease(r), + F.Bind2nd(mmap, F.Constant1[ANY](a)), + ) + }) + }, + ) + } + } +} diff --git a/v2/internal/foldable/types.go b/v2/internal/foldable/types.go new file mode 100644 index 0000000..5d63602 --- /dev/null +++ b/v2/internal/foldable/types.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package foldable + +import ( + M "github.com/IBM/fp-go/v2/monoid" +) + +type Foldable[A, B, HKTA any] interface { + Reduce(func(B, A) B, B) func(HKTA) B + ReduceRight(func(B, A) B, B) func(HKTA) B + FoldMap(m M.Monoid[B]) func(func(A) B) func(HKTA) B +} diff --git a/v2/internal/fromeither/either.go b/v2/internal/fromeither/either.go new file mode 100644 index 0000000..27a04d5 --- /dev/null +++ b/v2/internal/fromeither/either.go @@ -0,0 +1,93 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fromeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + O "github.com/IBM/fp-go/v2/option" +) + +func FromOption[A, HKTEA, E any](fromEither func(ET.Either[E, A]) HKTEA, onNone func() E) func(ma O.Option[A]) HKTEA { + return F.Flow2(ET.FromOption[A](onNone), fromEither) +} + +func FromPredicate[E, A, HKTEA any](fromEither func(ET.Either[E, A]) HKTEA, pred func(A) bool, onFalse func(A) E) func(A) HKTEA { + return F.Flow2(ET.FromPredicate(pred, onFalse), fromEither) +} + +func MonadFromOption[E, A, HKTEA any]( + fromEither func(ET.Either[E, A]) HKTEA, + onNone func() E, + ma O.Option[A], +) HKTEA { + return F.Pipe1( + O.MonadFold( + ma, + F.Nullary2(onNone, ET.Left[A, E]), + ET.Right[E, A], + ), + fromEither, + ) +} + +func FromOptionK[A, E, B, HKTEB any]( + fromEither func(ET.Either[E, B]) HKTEB, + onNone func() E) func(f func(A) O.Option[B]) func(A) HKTEB { + // helper + return F.Bind2nd(F.Flow2[func(A) O.Option[B], func(O.Option[B]) HKTEB, A, O.Option[B], HKTEB], FromOption(fromEither, onNone)) +} + +func MonadChainEitherK[A, E, B, HKTEA, HKTEB any]( + mchain func(HKTEA, func(A) HKTEB) HKTEB, + fromEither func(ET.Either[E, B]) HKTEB, + ma HKTEA, + f func(A) ET.Either[E, B]) HKTEB { + return mchain(ma, F.Flow2(f, fromEither)) +} + +func ChainEitherK[A, E, B, HKTEA, HKTEB any]( + mchain func(func(A) HKTEB) func(HKTEA) HKTEB, + fromEither func(ET.Either[E, B]) HKTEB, + f func(A) ET.Either[E, B]) func(HKTEA) HKTEB { + return mchain(F.Flow2(f, fromEither)) +} + +func ChainOptionK[A, E, B, HKTEA, HKTEB any]( + mchain func(HKTEA, func(A) HKTEB) HKTEB, + fromEither func(ET.Either[E, B]) HKTEB, + onNone func() E, +) func(f func(A) O.Option[B]) func(ma HKTEA) HKTEB { + return F.Flow2(FromOptionK[A](fromEither, onNone), F.Bind1st(F.Bind2nd[HKTEA, func(A) HKTEB, HKTEB], mchain)) +} + +func MonadChainFirstEitherK[A, E, B, HKTEA, HKTEB any]( + mchain func(HKTEA, func(A) HKTEA) HKTEA, + mmap func(HKTEB, func(B) A) HKTEA, + fromEither func(ET.Either[E, B]) HKTEB, + ma HKTEA, + f func(A) ET.Either[E, B]) HKTEA { + return C.MonadChainFirst(mchain, mmap, ma, F.Flow2(f, fromEither)) +} + +func ChainFirstEitherK[A, E, B, HKTEA, HKTEB any]( + mchain func(func(A) HKTEA) func(HKTEA) HKTEA, + mmap func(func(B) A) func(HKTEB) HKTEA, + fromEither func(ET.Either[E, B]) HKTEB, + f func(A) ET.Either[E, B]) func(HKTEA) HKTEA { + return C.ChainFirst(mchain, mmap, F.Flow2(f, fromEither)) +} diff --git a/v2/internal/fromeither/types.go b/v2/internal/fromeither/types.go new file mode 100644 index 0000000..39c7b00 --- /dev/null +++ b/v2/internal/fromeither/types.go @@ -0,0 +1,24 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fromeither + +import ( + ET "github.com/IBM/fp-go/v2/either" +) + +type FromEither[E, A, HKTA any] interface { + FromEither(ET.Either[E, A]) HKTA +} diff --git a/v2/internal/fromio/io.go b/v2/internal/fromio/io.go new file mode 100644 index 0000000..e26d009 --- /dev/null +++ b/v2/internal/fromio/io.go @@ -0,0 +1,55 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fromio + +import ( + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" +) + +func MonadChainFirstIOK[A, B, HKTA, HKTB any, GIOB ~func() B]( + mchain func(HKTA, func(A) HKTA) HKTA, + mmap func(HKTB, func(B) A) HKTA, + fromio func(GIOB) HKTB, + first HKTA, f func(A) GIOB) HKTA { + // chain + return C.MonadChainFirst(mchain, mmap, first, F.Flow2(f, fromio)) +} + +func ChainFirstIOK[A, B, HKTA, HKTB any, GIOB ~func() B]( + mchain func(func(A) HKTA) func(HKTA) HKTA, + mmap func(func(B) A) func(HKTB) HKTA, + fromio func(GIOB) HKTB, + f func(A) GIOB) func(HKTA) HKTA { + // chain + return C.ChainFirst(mchain, mmap, F.Flow2(f, fromio)) +} + +func MonadChainIOK[GR ~func() B, A, B, HKTA, HKTB any]( + mchain func(HKTA, func(A) HKTB) HKTB, + fromio func(GR) HKTB, + first HKTA, f func(A) GR) HKTB { + // chain + return C.MonadChain[A, B](mchain, first, F.Flow2(f, fromio)) +} + +func ChainIOK[GR ~func() B, A, B, HKTA, HKTB any]( + mchain func(func(A) HKTB) func(HKTA) HKTB, + fromio func(GR) HKTB, + f func(A) GR) func(HKTA) HKTB { + // chain + return C.Chain[A, B](mchain, F.Flow2(f, fromio)) +} diff --git a/v2/internal/fromio/types.go b/v2/internal/fromio/types.go new file mode 100644 index 0000000..d2fc404 --- /dev/null +++ b/v2/internal/fromio/types.go @@ -0,0 +1,20 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fromio + +type FromIO[A, GA ~func() A, HKTA any] interface { + FromIO(GA) HKTA +} diff --git a/v2/internal/fromioeither/ioeither.go b/v2/internal/fromioeither/ioeither.go new file mode 100644 index 0000000..67a760c --- /dev/null +++ b/v2/internal/fromioeither/ioeither.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fromioeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" +) + +func MonadChainFirstIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any]( + mchain func(HKTA, func(A) HKTA) HKTA, + mmap func(HKTB, func(B) A) HKTA, + fromio func(GIOB) HKTB, + first HKTA, f func(A) GIOB) HKTA { + // chain + return C.MonadChainFirst(mchain, mmap, first, F.Flow2(f, fromio)) +} + +func ChainFirstIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any]( + mchain func(func(A) HKTA) func(HKTA) HKTA, + mmap func(func(B) A) func(HKTB) HKTA, + fromio func(GIOB) HKTB, + f func(A) GIOB) func(HKTA) HKTA { + // chain + return C.ChainFirst(mchain, mmap, F.Flow2(f, fromio)) +} + +func MonadChainIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any]( + mchain func(HKTA, func(A) HKTB) HKTB, + fromio func(GIOB) HKTB, + first HKTA, f func(A) GIOB) HKTB { + // chain + return C.MonadChain[A, B](mchain, first, F.Flow2(f, fromio)) +} + +func ChainIOEitherK[GIOB ~func() ET.Either[E, B], E, A, B, HKTA, HKTB any]( + mchain func(func(A) HKTB) func(HKTA) HKTB, + fromio func(GIOB) HKTB, + f func(A) GIOB) func(HKTA) HKTB { + // chain + return C.Chain[A, B](mchain, F.Flow2(f, fromio)) +} diff --git a/v2/internal/fromreader/reader.go b/v2/internal/fromreader/reader.go new file mode 100644 index 0000000..26b1082 --- /dev/null +++ b/v2/internal/fromreader/reader.go @@ -0,0 +1,54 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fromreader + +import ( + F "github.com/IBM/fp-go/v2/function" + G "github.com/IBM/fp-go/v2/reader/generic" +) + +func Ask[GR ~func(R) R, R, HKTRA any](fromReader func(GR) HKTRA) func() HKTRA { + return func() HKTRA { + return fromReader(G.Ask[GR]()) + } +} + +func Asks[GA ~func(R) A, R, A, HKTRA any](fromReader func(GA) HKTRA) func(GA) HKTRA { + return fromReader +} + +func FromReaderK[GB ~func(R) B, R, A, B, HKTRB any]( + fromReader func(GB) HKTRB, + f func(A) GB) func(A) HKTRB { + return F.Flow2(f, fromReader) +} + +func MonadChainReaderK[GB ~func(R) B, R, A, B, HKTRA, HKTRB any]( + mchain func(HKTRA, func(A) HKTRB) HKTRB, + fromReader func(GB) HKTRB, + ma HKTRA, + f func(A) GB, +) HKTRB { + return mchain(ma, FromReaderK(fromReader, f)) +} + +func ChainReaderK[GB ~func(R) B, R, A, B, HKTRA, HKTRB any]( + mchain func(HKTRA, func(A) HKTRB) HKTRB, + fromReader func(GB) HKTRB, + f func(A) GB, +) func(HKTRA) HKTRB { + return F.Bind2nd(mchain, FromReaderK(fromReader, f)) +} diff --git a/v2/internal/functor/flap.go b/v2/internal/functor/flap.go new file mode 100644 index 0000000..c8c2a48 --- /dev/null +++ b/v2/internal/functor/flap.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package functor + +func flap[FAB ~func(A) B, A, B any](a A) func(FAB) B { + return func(f FAB) B { + return f(a) + } +} + +func MonadFlap[FAB ~func(A) B, A, B, HKTFAB, HKTB any]( + fmap func(HKTFAB, func(FAB) B) HKTB, + + fab HKTFAB, + a A, +) HKTB { + return fmap(fab, flap[FAB, A, B](a)) +} + +func Flap[FAB ~func(A) B, A, B, HKTFAB, HKTB any]( + fmap func(func(FAB) B) func(HKTFAB) HKTB, + a A, +) func(HKTFAB) HKTB { + return fmap(flap[FAB, A, B](a)) +} diff --git a/v2/internal/functor/functor.go b/v2/internal/functor/functor.go new file mode 100644 index 0000000..a602e43 --- /dev/null +++ b/v2/internal/functor/functor.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package functor + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// HKTFGA = HKT[F, HKT[G, A]] +// HKTFGB = HKT[F, HKT[G, B]] +func MonadMap[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any]( + fmap func(HKTFGA, func(HKTGA) HKTGB) HKTFGB, + gmap func(HKTGA, func(A) B) HKTGB, + fa HKTFGA, + f func(A) B) HKTFGB { + return fmap(fa, F.Bind2nd(gmap, f)) +} + +func Map[A, B, HKTGA, HKTGB, HKTFGA, HKTFGB any]( + fmap func(func(HKTGA) HKTGB) func(HKTFGA) HKTFGB, + gmap func(func(A) B) func(HKTGA) HKTGB, + f func(A) B) func(HKTFGA) HKTFGB { + return fmap(gmap(f)) +} + +func MonadLet[S1, S2, B, HKTS1, HKTS2 any]( + mmap func(HKTS1, func(S1) S2) HKTS2, + first HKTS1, + key func(B) func(S1) S2, + f func(S1) B, +) HKTS2 { + return mmap(first, func(s1 S1) S2 { + return key(f(s1))(s1) + }) +} + +func Let[S1, S2, B, HKTS1, HKTS2 any]( + mmap func(func(S1) S2) func(HKTS1) HKTS2, + key func(B) func(S1) S2, + f func(S1) B, +) func(HKTS1) HKTS2 { + return mmap(func(s1 S1) S2 { + return key(f(s1))(s1) + }) +} + +func LetTo[S1, S2, B, HKTS1, HKTS2 any]( + mmap func(func(S1) S2) func(HKTS1) HKTS2, + key func(B) func(S1) S2, + b B, +) func(HKTS1) HKTS2 { + return mmap(key(b)) +} diff --git a/v2/internal/functor/testing/laws.go b/v2/internal/functor/testing/laws.go new file mode 100644 index 0000000..868b28e --- /dev/null +++ b/v2/internal/functor/testing/laws.go @@ -0,0 +1,143 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/stretchr/testify/assert" +) + +// Functor identity law +// +// F.map(fa, a => a) <-> fa +// +// Deprecated: use [FunctorAssertIdentity] +func AssertIdentity[HKTA, A any](t *testing.T, eq E.Eq[HKTA], fmap func(HKTA, func(A) A) HKTA) func(fa HKTA) bool { + t.Helper() + return func(fa HKTA) bool { + return assert.True(t, eq.Equals(fa, fmap(fa, F.Identity[A])), "Functor identity law") + } +} + +// Functor identity law +// +// F.map(fa, a => a) <-> fa +func FunctorAssertIdentity[HKTA, A any]( + t *testing.T, + eq E.Eq[HKTA], + + fca functor.Functor[A, A, HKTA, HKTA], +) func(fa HKTA) bool { + + t.Helper() + return func(fa HKTA) bool { + + return assert.True(t, eq.Equals(fa, fca.Map(F.Identity[A])(fa)), "Functor identity law") + } +} + +// Functor composition law +// +// F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc) +// +// Deprecated: use [FunctorAssertComposition] instead +func AssertComposition[HKTA, HKTB, HKTC, A, B, C any]( + t *testing.T, + + eq E.Eq[HKTC], + + fab func(HKTA, func(A) B) HKTB, + fac func(HKTA, func(A) C) HKTC, + fbc func(HKTB, func(B) C) HKTC, + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + t.Helper() + return func(fa HKTA) bool { + return assert.True(t, eq.Equals(fac(fa, F.Flow2(ab, bc)), fbc(fab(fa, ab), bc)), "Functor composition law") + } +} + +// Functor composition law +// +// F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc) +func FunctorAssertComposition[HKTA, HKTB, HKTC, A, B, C any]( + t *testing.T, + + eq E.Eq[HKTC], + + fab functor.Functor[A, B, HKTA, HKTB], + fac functor.Functor[A, C, HKTA, HKTC], + fbc functor.Functor[B, C, HKTB, HKTC], + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + t.Helper() + return func(fa HKTA) bool { + return assert.True(t, eq.Equals(fac.Map(F.Flow2(ab, bc))(fa), fbc.Map(bc)(fab.Map(ab)(fa))), "Functor composition law") + } +} + +// AssertLaws asserts the functor laws `identity` and `composition` +// +// Deprecated: use [FunctorAssertLaws] instead +func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqc E.Eq[HKTC], + + faa func(HKTA, func(A) A) HKTA, + fab func(HKTA, func(A) B) HKTB, + fac func(HKTA, func(A) C) HKTC, + fbc func(HKTB, func(B) C) HKTC, + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + t.Helper() + identity := AssertIdentity(t, eqa, faa) + composition := AssertComposition(t, eqc, fab, fac, fbc, ab, bc) + + return func(fa HKTA) bool { + return identity(fa) && composition(fa) + } +} + +// FunctorAssertLaws asserts the functor laws `identity` and `composition` +func FunctorAssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqc E.Eq[HKTC], + + faa functor.Functor[A, A, HKTA, HKTA], + fab functor.Functor[A, B, HKTA, HKTB], + fac functor.Functor[A, C, HKTA, HKTC], + fbc functor.Functor[B, C, HKTB, HKTC], + + ab func(A) B, + bc func(B) C, +) func(fa HKTA) bool { + t.Helper() + identity := FunctorAssertIdentity(t, eqa, faa) + composition := FunctorAssertComposition(t, eqc, fab, fac, fbc, ab, bc) + + return func(fa HKTA) bool { + return identity(fa) && composition(fa) + } +} diff --git a/v2/internal/functor/types.go b/v2/internal/functor/types.go new file mode 100644 index 0000000..022cde8 --- /dev/null +++ b/v2/internal/functor/types.go @@ -0,0 +1,20 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package functor + +type Functor[A, B, HKTA, HKTB any] interface { + Map(func(A) B) func(HKTA) HKTB +} diff --git a/v2/internal/lazy/memoize.go b/v2/internal/lazy/memoize.go new file mode 100644 index 0000000..348ef97 --- /dev/null +++ b/v2/internal/lazy/memoize.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import "sync" + +// Memoize computes the value of the provided IO monad lazily but exactly once +func Memoize[GA ~func() A, A any](ma GA) GA { + // synchronization primitives + var once sync.Once + var result A + // callback + gen := func() { + result = ma() + } + // returns our memoized wrapper + return func() A { + once.Do(gen) + return result + } +} diff --git a/v2/internal/monad/monad.go b/v2/internal/monad/monad.go new file mode 100644 index 0000000..287556c --- /dev/null +++ b/v2/internal/monad/monad.go @@ -0,0 +1,54 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monad + +import ( + "github.com/IBM/fp-go/v2/internal/applicative" + "github.com/IBM/fp-go/v2/internal/apply" + "github.com/IBM/fp-go/v2/internal/chain" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/pointed" +) + +type Monad[A, B, HKTA, HKTB, HKTFAB any] interface { + applicative.Applicative[A, B, HKTA, HKTB, HKTFAB] + chain.Chainable[A, B, HKTA, HKTB, HKTFAB] +} + +// ToFunctor converts from [Monad] to [functor.Functor] +func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] { + return ap +} + +// ToApply converts from [Monad] to [apply.Apply] +func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] { + return ap +} + +// ToPointed converts from [Monad] to [pointed.Pointed] +func ToPointed[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) pointed.Pointed[A, HKTA] { + return ap +} + +// ToApplicative converts from [Monad] to [applicative.Applicative] +func ToApplicative[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) applicative.Applicative[A, B, HKTA, HKTB, HKTFAB] { + return ap +} + +// ToChainable converts from [Monad] to [chain.Chainable] +func ToChainable[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) chain.Chainable[A, B, HKTA, HKTB, HKTFAB] { + return ap +} diff --git a/v2/internal/monad/testing/laws.go b/v2/internal/monad/testing/laws.go new file mode 100644 index 0000000..9621998 --- /dev/null +++ b/v2/internal/monad/testing/laws.go @@ -0,0 +1,228 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/eq" + "github.com/IBM/fp-go/v2/internal/applicative" + LA "github.com/IBM/fp-go/v2/internal/applicative/testing" + "github.com/IBM/fp-go/v2/internal/chain" + LC "github.com/IBM/fp-go/v2/internal/chain/testing" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" + "github.com/stretchr/testify/assert" +) + +// Apply monad left identity law +// +// M.chain(M.of(a), f) <-> f(a) +// +// Deprecated: use [MonadAssertLeftIdentity] instead +func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T, + eq E.Eq[HKTB], + + fofa func(A) HKTA, + fofb func(B) HKTB, + + fchain func(HKTA, func(A) HKTB) HKTB, + + ab func(A) B, +) func(a A) bool { + return func(a A) bool { + + f := func(a A) HKTB { + return fofb(ab(a)) + } + + left := fchain(fofa(a), f) + right := f(a) + + return assert.True(t, eq.Equals(left, right), "Monad left identity") + } +} + +// Apply monad left identity law +// +// M.chain(M.of(a), f) <-> f(a) +func MonadAssertLeftIdentity[HKTA, HKTB, HKTFAB, A, B any](t *testing.T, + eq E.Eq[HKTB], + + fofb pointed.Pointed[B, HKTB], + + ma monad.Monad[A, B, HKTA, HKTB, HKTFAB], + + ab func(A) B, +) func(a A) bool { + return func(a A) bool { + + f := func(a A) HKTB { + return fofb.Of(ab(a)) + } + + left := ma.Chain(f)(ma.Of(a)) + right := f(a) + + return assert.True(t, eq.Equals(left, right), "Monad left identity") + } +} + +// Apply monad right identity law +// +// M.chain(fa, M.of) <-> fa +// +// Deprecated: use [MonadAssertRightIdentity] instead +func AssertRightIdentity[HKTA, A any](t *testing.T, + eq E.Eq[HKTA], + + fofa func(A) HKTA, + + fchain func(HKTA, func(A) HKTA) HKTA, +) func(fa HKTA) bool { + return func(fa HKTA) bool { + + left := fchain(fa, fofa) + right := fa + + return assert.True(t, eq.Equals(left, right), "Monad right identity") + } +} + +// Apply monad right identity law +// +// M.chain(fa, M.of) <-> fa +func MonadAssertRightIdentity[HKTA, HKTAA, A any](t *testing.T, + eq E.Eq[HKTA], + + ma monad.Monad[A, A, HKTA, HKTA, HKTAA], + +) func(fa HKTA) bool { + return func(fa HKTA) bool { + + left := ma.Chain(ma.Of)(fa) + right := fa + + return assert.True(t, eq.Equals(left, right), "Monad right identity") + } +} + +// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange', `associativity`, `left identity`, `right identity` +// +// Deprecated: use [MonadAssertLaws] instead +func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqb E.Eq[HKTB], + eqc E.Eq[HKTC], + + fofa func(A) HKTA, + fofb func(B) HKTB, + fofc func(C) HKTC, + + fofaa func(func(A) A) HKTAA, + fofab func(func(A) B) HKTAB, + fofbc func(func(B) C) HKTBC, + fofabb func(func(func(A) B) B) HKTABB, + + faa func(HKTA, func(A) A) HKTA, + fab func(HKTA, func(A) B) HKTB, + fac func(HKTA, func(A) C) HKTC, + fbc func(HKTB, func(B) C) HKTC, + + fmap func(HKTBC, func(func(B) C) func(func(A) B) func(A) C) HKTABAC, + + chainaa func(HKTA, func(A) HKTA) HKTA, + chainab func(HKTA, func(A) HKTB) HKTB, + chainac func(HKTA, func(A) HKTC) HKTC, + chainbc func(HKTB, func(B) HKTC) HKTC, + + fapaa func(HKTAA, HKTA) HKTA, + fapab func(HKTAB, HKTA) HKTB, + fapbc func(HKTBC, HKTB) HKTC, + fapac func(HKTAC, HKTA) HKTC, + + fapabb func(HKTABB, HKTAB) HKTB, + fapabac func(HKTABAC, HKTAB) HKTAC, + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + // applicative laws + applicative := LA.AssertLaws(t, eqa, eqb, eqc, fofa, fofb, fofaa, fofab, fofbc, fofabb, faa, fab, fac, fbc, fmap, fapaa, fapab, fapbc, fapac, fapabb, fapabac, ab, bc) + // chain laws + chain := LC.AssertLaws(t, eqa, eqc, fofb, fofc, fofab, fofbc, faa, fab, fac, fbc, fmap, chainab, chainac, chainbc, fapab, fapbc, fapac, fapabac, ab, bc) + // monad laws + leftIdentity := AssertLeftIdentity(t, eqb, fofa, fofb, chainab, ab) + rightIdentity := AssertRightIdentity(t, eqa, fofa, chainaa) + + return func(a A) bool { + fa := fofa(a) + return applicative(a) && chain(fa) && leftIdentity(a) && rightIdentity(fa) + } +} + +// MonadAssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange', `associativity`, `left identity`, `right identity` +func MonadAssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T, + eqa E.Eq[HKTA], + eqb E.Eq[HKTB], + eqc E.Eq[HKTC], + + fofc pointed.Pointed[C, HKTC], + fofaa pointed.Pointed[func(A) A, HKTAA], + fofbc pointed.Pointed[func(B) C, HKTBC], + fofabb pointed.Pointed[func(func(A) B) B, HKTABB], + + fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC], + + fapabb applicative.Applicative[func(A) B, B, HKTAB, HKTB, HKTABB], + fapabac applicative.Applicative[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC], + + maa monad.Monad[A, A, HKTA, HKTA, HKTAA], + mab monad.Monad[A, B, HKTA, HKTB, HKTAB], + mac monad.Monad[A, C, HKTA, HKTC, HKTAC], + mbc monad.Monad[B, C, HKTB, HKTC, HKTBC], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + // derivations + fofa := monad.ToPointed(maa) + fofb := monad.ToPointed(mbc) + fofab := applicative.ToPointed(fapabb) + fapaa := monad.ToApplicative(maa) + fapab := monad.ToApplicative(mab) + chainab := monad.ToChainable(mab) + chainac := monad.ToChainable(mac) + chainbc := monad.ToChainable(mbc) + fapbc := chain.ToApply(chainbc) + fapac := chain.ToApply(chainac) + + faa := monad.ToFunctor(maa) + + // applicative laws + apLaw := LA.ApplicativeAssertLaws(t, eqa, eqb, eqc, fofb, fofaa, fofbc, fofabb, faa, fmap, fapaa, fapab, fapbc, fapac, fapabb, fapabac, ab, bc) + // chain laws + chainLaw := LC.ChainAssertLaws(t, eqa, eqc, fofb, fofc, fofab, fofbc, faa, fmap, chainab, chainac, chainbc, applicative.ToApply(fapabac), ab, bc) + // monad laws + leftIdentity := MonadAssertLeftIdentity(t, eqb, fofb, mab, ab) + rightIdentity := MonadAssertRightIdentity(t, eqa, maa) + + return func(a A) bool { + fa := fofa.Of(a) + return apLaw(a) && chainLaw(fa) && leftIdentity(a) && rightIdentity(fa) + } +} diff --git a/v2/internal/optiont/option.go b/v2/internal/optiont/option.go new file mode 100644 index 0000000..5df0952 --- /dev/null +++ b/v2/internal/optiont/option.go @@ -0,0 +1,125 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package optiont + +import ( + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/apply" + FC "github.com/IBM/fp-go/v2/internal/functor" + O "github.com/IBM/fp-go/v2/option" +) + +func Of[A, HKTA any](fof func(O.Option[A]) HKTA, a A) HKTA { + return F.Pipe2(a, O.Of[A], fof) +} + +func None[A, HKTA any](fof func(O.Option[A]) HKTA) HKTA { + return F.Pipe1(O.None[A](), fof) +} + +func OfF[A, HKTA, HKTEA any](fmap func(HKTA, func(A) O.Option[A]) HKTEA, fa HKTA) HKTEA { + return fmap(fa, O.Of[A]) +} + +func MonadMap[A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(O.Option[A]) O.Option[B]) HKTFB, fa HKTFA, f func(A) B) HKTFB { + // HKTGA = Either[E, A] + // HKTGB = Either[E, B] + return FC.MonadMap(fmap, O.MonadMap[A, B], fa, f) +} + +func Map[A, B, HKTFA, HKTFB any](fmap func(func(O.Option[A]) O.Option[B]) func(HKTFA) HKTFB, f func(A) B) func(HKTFA) HKTFB { + // HKTGA = Either[E, A] + // HKTGB = Either[E, B] + return FC.Map(fmap, O.Map[A, B], f) +} + +func MonadChain[A, B, HKTFA, HKTFB any]( + fchain func(HKTFA, func(O.Option[A]) HKTFB) HKTFB, + fof func(O.Option[B]) HKTFB, + ma HKTFA, + f func(A) HKTFB) HKTFB { + // dispatch to the even more generic implementation + return fchain(ma, O.Fold(F.Nullary2(O.None[B], fof), f)) +} + +func Chain[A, B, HKTFA, HKTFB any]( + fchain func(func(O.Option[A]) HKTFB) func(HKTFA) HKTFB, + fof func(O.Option[B]) HKTFB, + f func(A) HKTFB) func(ma HKTFA) HKTFB { + // dispatch to the even more generic implementation + return fchain(O.Fold(F.Nullary2(O.None[B], fof), f)) +} + +func MonadAp[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any]( + fap func(HKTFGAB, HKTFA) HKTFB, + fmap func(HKTFAB, func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) HKTFGAB, + fab HKTFAB, + fa HKTFA) HKTFB { + return apply.MonadAp(fap, fmap, O.MonadAp[B, A], fab, fa) +} + +func Ap[A, B, HKTFAB, HKTFGAB, HKTFA, HKTFB any]( + fap func(HKTFA) func(HKTFGAB) HKTFB, + fmap func(func(O.Option[func(A) B]) func(O.Option[A]) O.Option[B]) func(HKTFAB) HKTFGAB, + fa HKTFA) func(HKTFAB) HKTFB { + return apply.Ap(fap, fmap, O.Ap[B, A], fa) +} + +func MatchE[A, HKTEA, HKTB any](mchain func(HKTEA, func(O.Option[A]) HKTB) HKTB, onNone func() HKTB, onSome func(A) HKTB) func(HKTEA) HKTB { + return F.Bind2nd(mchain, O.Fold(onNone, onSome)) +} + +func FromOptionK[A, B, HKTB any]( + fof func(O.Option[B]) HKTB, + f func(A) O.Option[B]) func(A) HKTB { + return F.Flow2(f, fof) +} + +func MonadChainOptionK[A, B, HKTA, HKTB any]( + fchain func(HKTA, func(O.Option[A]) HKTB) HKTB, + fof func(O.Option[B]) HKTB, + ma HKTA, + f func(A) O.Option[B], +) HKTB { + return MonadChain(fchain, fof, ma, FromOptionK(fof, f)) +} + +func ChainOptionK[A, B, HKTA, HKTB any]( + fchain func(func(O.Option[A]) HKTB) func(HKTA) HKTB, + fof func(O.Option[B]) HKTB, + f func(A) O.Option[B], +) func(HKTA) HKTB { + return Chain(fchain, fof, FromOptionK(fof, f)) +} + +func MonadAlt[LAZY ~func() HKTFA, A, HKTFA any]( + fof func(O.Option[A]) HKTFA, + fchain func(HKTFA, func(O.Option[A]) HKTFA) HKTFA, + + first HKTFA, + second LAZY) HKTFA { + + return fchain(first, O.Fold(second, F.Flow2(O.Of[A], fof))) +} + +func Alt[LAZY ~func() HKTFA, A, HKTFA any]( + fof func(O.Option[A]) HKTFA, + fchain func(func(O.Option[A]) HKTFA) func(HKTFA) HKTFA, + + second LAZY) func(HKTFA) HKTFA { + + return fchain(O.Fold(second, F.Flow2(O.Of[A], fof))) +} diff --git a/v2/internal/pointed/types.go b/v2/internal/pointed/types.go new file mode 100644 index 0000000..944692a --- /dev/null +++ b/v2/internal/pointed/types.go @@ -0,0 +1,21 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pointed + +type Pointed[A, HKTA any] interface { + // Of lifts a value into its higher kinded type + Of(A) HKTA +} diff --git a/v2/internal/readert/reader.go b/v2/internal/readert/reader.go new file mode 100644 index 0000000..0ce1b0d --- /dev/null +++ b/v2/internal/readert/reader.go @@ -0,0 +1,74 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readert + +import ( + F "github.com/IBM/fp-go/v2/function" + R "github.com/IBM/fp-go/v2/reader/generic" +) + +// here we implement the monadic operations using callbacks from +// higher kinded types, as good a golang allows use to do this + +func MonadMap[GEA ~func(E) HKTA, GEB ~func(E) HKTB, E, A, B, HKTA, HKTB any]( + fmap func(HKTA, func(A) B) HKTB, + fa GEA, + f func(A) B, +) GEB { + return R.MonadMap[GEA, GEB](fa, F.Bind2nd(fmap, f)) +} + +func Map[GEA ~func(E) HKTA, GEB ~func(E) HKTB, E, A, B, HKTA, HKTB any]( + fmap func(func(A) B) func(HKTA) HKTB, + f func(A) B, +) func(GEA) GEB { + return F.Pipe2( + f, + fmap, + R.Map[GEA, GEB, E, HKTA, HKTB], + ) +} + +func MonadChain[GEA ~func(E) HKTA, GEB ~func(E) HKTB, A, E, HKTA, HKTB any](fchain func(HKTA, func(A) HKTB) HKTB, ma GEA, f func(A) GEB) GEB { + return R.MakeReader(func(r E) HKTB { + return fchain(ma(r), func(a A) HKTB { + return f(a)(r) + }) + }) +} + +func MonadOf[GEA ~func(E) HKTA, E, A, HKTA any](fof func(A) HKTA, a A) GEA { + return R.MakeReader(func(_ E) HKTA { + return fof(a) + }) +} + +// HKTFAB = HKT[func(A)B] +func MonadAp[GEA ~func(E) HKTA, GEB ~func(E) HKTB, GEFAB ~func(E) HKTFAB, E, A, HKTA, HKTB, HKTFAB any](fap func(HKTFAB, HKTA) HKTB, fab GEFAB, fa GEA) GEB { + return R.MakeReader(func(r E) HKTB { + return fap(fab(r), fa(r)) + }) +} + +func MonadFromReader[GA ~func(E) A, GEA ~func(E) HKTA, E, A, HKTA any]( + fof func(A) HKTA, ma GA) GEA { + return R.MakeReader(F.Flow2(ma, fof)) +} + +func FromReader[GA ~func(E) A, GEA ~func(E) HKTA, E, A, HKTA any]( + fof func(A) HKTA) func(ma GA) GEA { + return F.Bind1st(MonadFromReader[GA, GEA, E, A, HKTA], fof) +} diff --git a/v2/internal/record/record.go b/v2/internal/record/record.go new file mode 100644 index 0000000..9dbffc5 --- /dev/null +++ b/v2/internal/record/record.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +func Reduce[M ~map[K]V, K comparable, V, R any](r M, f func(R, V) R, initial R) R { + current := initial + for _, v := range r { + current = f(current, v) + } + return current +} + +func ReduceWithIndex[M ~map[K]V, K comparable, V, R any](r M, f func(K, R, V) R, initial R) R { + current := initial + for k, v := range r { + current = f(k, current, v) + } + return current +} + +func ReduceRef[M ~map[K]V, K comparable, V, R any](r M, f func(R, *V) R, initial R) R { + current := initial + for _, v := range r { + current = f(current, &v) // #nosec G601 + } + return current +} + +func ReduceRefWithIndex[M ~map[K]V, K comparable, V, R any](r M, f func(K, R, *V) R, initial R) R { + current := initial + for k, v := range r { + current = f(k, current, &v) // #nosec G601 + } + return current +} diff --git a/v2/internal/record/traverse.go b/v2/internal/record/traverse.go new file mode 100644 index 0000000..030ed13 --- /dev/null +++ b/v2/internal/record/traverse.go @@ -0,0 +1,109 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// createEmpty creates a new empty, read-write map +// this is different to Empty which creates a new read-only empty map +func createEmpty[N ~map[K]A, K comparable, A any]() N { + return make(N) +} + +// inserts the key/value pair into a read-write map for performance +// order of parameters is adjusted to be curryable +func addKey[N ~map[K]A, K comparable, A any](key K, m N, value A) N { + m[key] = value + return m +} + +/* +* +We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces + +HKTRB = HKT +HKTA = HKT +HKTB = HKT +HKTAB = HKT +*/ +func traverseWithIndex[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any]( + fof func(MB) HKTRB, + fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + ta MA, f func(K, A) HKTB) HKTRB { + // this function inserts a value into a map with a given key + mmap := F.Flow2(F.Curry3(addKey[MB, K, B]), fmap) + + return ReduceWithIndex(ta, func(k K, r HKTRB, a A) HKTRB { + return F.Pipe2( + r, + mmap(k), + fap(f(k, a)), + ) + }, fof(createEmpty[MB]())) +} + +func MonadTraverse[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any]( + fof func(MB) HKTRB, + fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + r MA, f func(A) HKTB) HKTRB { + return traverseWithIndex(fof, fmap, fap, r, F.Ignore1of2[K](f)) +} + +func TraverseWithIndex[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any]( + fof func(MB) HKTRB, + fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + f func(K, A) HKTB) func(MA) HKTRB { + + return func(ma MA) HKTRB { + return traverseWithIndex(fof, fmap, fap, ma, f) + } +} + +// HKTA = HKT +// HKTB = HKT +// HKTAB = HKT +// HKTRB = HKT +func Traverse[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any]( + fof func(MB) HKTRB, + fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + f func(A) HKTB) func(MA) HKTRB { + + return func(ma MA) HKTRB { + return MonadTraverse(fof, fmap, fap, ma, f) + } +} + +// HKTA = HKT[A] +// HKTAA = HKT[func(A)MA] +// HKTRA = HKT[MA] +func Sequence[MA ~map[K]A, MKTA ~map[K]HKTA, K comparable, A, HKTA, HKTAA, HKTRA any]( + fof func(MA) HKTRA, + fmap func(func(MA) func(A) MA) func(HKTRA) HKTAA, + fap func(HKTA) func(HKTAA) HKTRA, + + ma MKTA) HKTRA { + return MonadTraverse(fof, fmap, fap, ma, F.Identity[HKTA]) +} diff --git a/v2/internal/statet/state.go b/v2/internal/statet/state.go new file mode 100644 index 0000000..f3fad5e --- /dev/null +++ b/v2/internal/statet/state.go @@ -0,0 +1,198 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package state + +import ( + F "github.com/IBM/fp-go/v2/function" + P "github.com/IBM/fp-go/v2/pair" +) + +func Of[ + HKTSA ~func(S) HKTA, + HKTA, + S, A any, +]( + fof func(P.Pair[A, S]) HKTA, + + a A) HKTSA { + + return F.Flow2( + F.Bind1st(P.MakePair[A, S], a), + fof, + ) +} + +func MonadMap[ + HKTSA ~func(S) HKTA, + HKTSB ~func(S) HKTB, + HKTA, + HKTB, + S, A, B any, +]( + fmap func(HKTA, func(P.Pair[A, S]) P.Pair[B, S]) HKTB, + + fa HKTSA, + f func(A) B, +) HKTSB { + + return F.Flow2( + fa, + F.Bind2nd(fmap, P.Map[S](f)), + ) +} + +func Map[ + HKTSA ~func(S) HKTA, + HKTSB ~func(S) HKTB, + HKTA, + HKTB, + S, A, B any, +]( + fmap func(func(P.Pair[A, S]) P.Pair[B, S]) func(HKTA) HKTB, + + f func(A) B, +) func(HKTSA) HKTSB { + mp := fmap(P.Map[S](f)) + + return func(fa HKTSA) HKTSB { + return F.Flow2( + fa, + mp, + ) + } +} + +func MonadChain[ + HKTSA ~func(S) HKTA, + HKTSB ~func(S) HKTB, + HKTA, + HKTB, + S, A any, +]( + fchain func(HKTA, func(P.Pair[A, S]) HKTB) HKTB, + + fa HKTSA, + f func(A) HKTSB, +) HKTSB { + return F.Flow2( + fa, + F.Bind2nd(fchain, func(a P.Pair[A, S]) HKTB { + return f(P.Head(a))(P.Tail(a)) + }), + ) +} + +func Chain[ + HKTSA ~func(S) HKTA, + HKTSB ~func(S) HKTB, + HKTA, + HKTB, + S, A any, +]( + fchain func(func(P.Pair[A, S]) HKTB) func(HKTA) HKTB, + + f func(A) HKTSB, +) func(HKTSA) HKTSB { + mp := fchain(func(a P.Pair[A, S]) HKTB { + return f(P.Head(a))(P.Tail(a)) + }) + + return func(fa HKTSA) HKTSB { + return F.Flow2( + fa, + mp, + ) + } +} + +func MonadAp[ + HKTSA ~func(S) HKTA, + HKTSB ~func(S) HKTB, + HKTSAB ~func(S) HKTAB, + HKTA, + HKTB, + HKTAB, + + S, A, B any, +]( + fmap func(HKTA, func(P.Pair[A, S]) P.Pair[B, S]) HKTB, + fchain func(HKTAB, func(P.Pair[func(A) B, S]) HKTB) HKTB, + + fab HKTSAB, + fa HKTSA, +) HKTSB { + return func(s S) HKTB { + return fchain(fab(s), func(ab P.Pair[func(A) B, S]) HKTB { + return fmap(fa(P.Tail(ab)), P.Map[S](P.Head(ab))) + }) + } +} + +func Ap[ + HKTSA ~func(S) HKTA, + HKTSB ~func(S) HKTB, + HKTSAB ~func(S) HKTAB, + HKTA, + HKTB, + HKTAB, + + S, A, B any, +]( + fmap func(func(P.Pair[A, S]) P.Pair[B, S]) func(HKTA) HKTB, + fchain func(func(P.Pair[func(A) B, S]) HKTB) func(HKTAB) HKTB, + + fa HKTSA, +) func(HKTSAB) HKTSB { + return func(fab HKTSAB) HKTSB { + return F.Flow2( + fab, + fchain(func(ab P.Pair[func(A) B, S]) HKTB { + return fmap(P.Map[S](P.Head(ab)))(fa(P.Tail(ab))) + }), + ) + } +} + +func FromF[ + HKTSA ~func(S) HKTA, + HKTA, + + HKTFA, + + S, A any, +]( + fmap func(HKTFA, func(A) P.Pair[A, S]) HKTA, + ma HKTFA) HKTSA { + + f1 := F.Bind1st(fmap, ma) + + return func(s S) HKTA { + return f1(F.Bind2nd(P.MakePair[A, S], s)) + } +} + +func FromState[ + HKTSA ~func(S) HKTA, + ST ~func(S) P.Pair[A, S], + HKTA, + + S, A any, +]( + fof func(P.Pair[A, S]) HKTA, + sa ST, +) HKTSA { + return F.Flow2(sa, fof) +} diff --git a/v2/internal/testing/sequence.go b/v2/internal/testing/sequence.go new file mode 100644 index 0000000..da09312 --- /dev/null +++ b/v2/internal/testing/sequence.go @@ -0,0 +1,183 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/pointed" + "github.com/stretchr/testify/assert" +) + +// SequenceArrayTest tests if the sequence operation works in case the operation cannot error +func SequenceArrayTest[ + HKTA, + HKTB, + HKTAA any, // HKT[[]A] +]( + eq EQ.Eq[HKTB], + + pa pointed.Pointed[string, HKTA], + pb pointed.Pointed[bool, HKTB], + faa functor.Functor[[]string, bool, HKTAA, HKTB], + seq func([]HKTA) HKTAA, +) func(count int) func(t *testing.T) { + + return func(count int) func(t *testing.T) { + + exp := make([]string, count) + good := make([]HKTA, count) + for i := 0; i < count; i++ { + val := fmt.Sprintf("TestData %d", i) + exp[i] = val + good[i] = pa.Of(val) + } + + return func(t *testing.T) { + res := F.Pipe2( + good, + seq, + faa.Map(func(act []string) bool { + return assert.Equal(t, exp, act) + }), + ) + assert.True(t, eq.Equals(res, pb.Of(true))) + } + } +} + +// SequenceArrayErrorTest tests if the sequence operation works in case the operation can error +func SequenceArrayErrorTest[ + HKTA, + HKTB, + HKTAA any, // HKT[[]A] +]( + eq EQ.Eq[HKTB], + + left func(error) HKTA, + leftB func(error) HKTB, + pa pointed.Pointed[string, HKTA], + pb pointed.Pointed[bool, HKTB], + faa functor.Functor[[]string, bool, HKTAA, HKTB], + seq func([]HKTA) HKTAA, +) func(count int) func(t *testing.T) { + + return func(count int) func(t *testing.T) { + + expGood := make([]string, count) + good := make([]HKTA, count) + expBad := make([]error, count) + bad := make([]HKTA, count) + + for i := 0; i < count; i++ { + goodVal := fmt.Sprintf("TestData %d", i) + badVal := fmt.Errorf("ErrorData %d", i) + expGood[i] = goodVal + good[i] = pa.Of(goodVal) + expBad[i] = badVal + bad[i] = left(badVal) + } + + total := 1 << count + + return func(t *testing.T) { + // test the good case + res := F.Pipe2( + good, + seq, + faa.Map(func(act []string) bool { + return assert.Equal(t, expGood, act) + }), + ) + assert.True(t, eq.Equals(res, pb.Of(true))) + // iterate and test the bad cases + for i := 1; i < total; i++ { + // run the test + t.Run(fmt.Sprintf("Bitmask test %d", i), func(t1 *testing.T) { + // the actual + act := make([]HKTA, count) + // the expected error + var exp error + // prepare the values bases on the bit mask + mask := 1 + for j := 0; j < count; j++ { + if (i & mask) == 0 { + act[j] = good[j] + } else { + act[j] = bad[j] + if exp == nil { + exp = expBad[j] + } + } + mask <<= 1 + } + // test the good case + res := F.Pipe2( + act, + seq, + faa.Map(func(act []string) bool { + return assert.Equal(t, expGood, act) + }), + ) + // validate the error + assert.True(t, eq.Equals(res, leftB(exp))) + }) + } + } + } +} + +// SequenceRecordTest tests if the sequence operation works in case the operation cannot error +func SequenceRecordTest[ + HKTA, + HKTB, + HKTAA any, // HKT[map[string]string] +]( + eq EQ.Eq[HKTB], + + pa pointed.Pointed[string, HKTA], + pb pointed.Pointed[bool, HKTB], + faa functor.Functor[map[string]string, bool, HKTAA, HKTB], + seq func(map[string]HKTA) HKTAA, +) func(count int) func(t *testing.T) { + + return func(count int) func(t *testing.T) { + + exp := make(map[string]string) + good := make(map[string]HKTA) + for i := 0; i < count; i++ { + key := fmt.Sprintf("KeyData %d", i) + val := fmt.Sprintf("ValueData %d", i) + exp[key] = val + good[key] = pa.Of(val) + } + + return func(t *testing.T) { + res := F.Pipe2( + good, + seq, + faa.Map(func(act map[string]string) bool { + return assert.Equal(t, exp, act) + }), + ) + assert.True(t, eq.Equals(res, pb.Of(true))) + } + } +} diff --git a/v2/internal/utils/do.go b/v2/internal/utils/do.go new file mode 100644 index 0000000..78b1e41 --- /dev/null +++ b/v2/internal/utils/do.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" +) + +type ( + Initial struct { + } + + WithLastName struct { + Initial + LastName string + } + + WithGivenName struct { + WithLastName + GivenName string + } +) + +var ( + Empty = Initial{} + + SetLastName = F.Curry2(func(name string, s1 Initial) WithLastName { + return WithLastName{ + Initial: s1, + LastName: name, + } + }) + + SetGivenName = F.Curry2(func(name string, s1 WithLastName) WithGivenName { + return WithGivenName{ + WithLastName: s1, + GivenName: name, + } + }) +) + +func GetFullName(s WithGivenName) string { + return fmt.Sprintf("%s %s", s.GivenName, s.LastName) +} diff --git a/v2/internal/utils/utils.go b/v2/internal/utils/utils.go new file mode 100644 index 0000000..9b8dd93 --- /dev/null +++ b/v2/internal/utils/utils.go @@ -0,0 +1,51 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "errors" + "strings" +) + +var Upper = strings.ToUpper + +func Inc(i int) int { + return i + 1 +} + +func Dec(i int) int { + return i - 1 +} + +func Sum(left, right int) int { + return left + right +} + +func Double(value int) int { + return value * 2 +} + +func Triple(value int) int { + return value * 3 +} + +func StringLen(value string) int { + return len(value) +} + +func Error() (int, error) { + return 0, errors.New("some error") +} diff --git a/v2/io/apply.go b/v2/io/apply.go new file mode 100644 index 0000000..ff80dba --- /dev/null +++ b/v2/io/apply.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + G "github.com/IBM/fp-go/v2/io/generic" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[IO[A]] { + return G.ApplySemigroup[IO[A]](s) +} + +func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[IO[A]] { + return G.ApplicativeMonoid[IO[A]](m) +} diff --git a/v2/io/bind.go b/v2/io/bind.go new file mode 100644 index 0000000..5364499 --- /dev/null +++ b/v2/io/bind.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) IO[S] { + return G.Do[IO[S], S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) IO[T], +) func(IO[S1]) IO[S2] { + return G.Bind[IO[S1], IO[S2], IO[T], S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(IO[S1]) IO[S2] { + return G.Let[IO[S1], IO[S2], S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(IO[S1]) IO[S2] { + return G.LetTo[IO[S1], IO[S2], S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func(IO[T]) IO[S1] { + return G.BindTo[IO[S1], IO[T], S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa IO[T], +) func(IO[S1]) IO[S2] { + return G.ApS[IO[S1], IO[S2], IO[T], S1, S2, T](setter, fa) +} diff --git a/v2/io/bind_test.go b/v2/io/bind_test.go new file mode 100644 index 0000000..e561499 --- /dev/null +++ b/v2/io/bind_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) IO[string] { + return Of("Doe") +} + +func getGivenName(s utils.WithLastName) IO[string] { + return Of("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(), "John Doe") +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + ApS(utils.SetLastName, Of("Doe")), + ApS(utils.SetGivenName, Of("John")), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(), "John Doe") +} diff --git a/v2/io/bracket.go b/v2/io/bracket.go new file mode 100644 index 0000000..2e1a970 --- /dev/null +++ b/v2/io/bracket.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[A, B, ANY any]( + acquire IO[A], + use func(A) IO[B], + release func(A, B) IO[ANY], +) IO[B] { + return G.Bracket(acquire, use, release) +} diff --git a/v2/io/doc.go b/v2/io/doc.go new file mode 100644 index 0000000..da6e141 --- /dev/null +++ b/v2/io/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +//go:generate go run .. io --count 10 --filename gen.go diff --git a/v2/io/eq.go b/v2/io/eq.go new file mode 100644 index 0000000..fb76a05 --- /dev/null +++ b/v2/io/eq.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Eq implements the equals predicate for values contained in the IO monad +func Eq[A any](e EQ.Eq[A]) EQ.Eq[IO[A]] { + return G.Eq[IO[A]](e) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[A comparable]() EQ.Eq[IO[A]] { + return G.FromStrictEquals[IO[A]]() +} diff --git a/v2/io/file/file.go b/v2/io/file/file.go new file mode 100644 index 0000000..934ff79 --- /dev/null +++ b/v2/io/file/file.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "io" + "os" + + IO "github.com/IBM/fp-go/v2/io" +) + +// Close closes a closeable resource and ignores a potential error +func Close[R io.Closer](r R) IO.IO[R] { + return IO.MakeIO[R](func() R { + r.Close() // #nosec: G104 + return r + }) +} + +// Remove removes a resource and ignores a potential error +func Remove(name string) IO.IO[string] { + return IO.MakeIO[string](func() string { + os.Remove(name) // #nosec: G104 + return name + }) +} diff --git a/v2/io/functor.go b/v2/io/functor.go new file mode 100644 index 0000000..b35b591 --- /dev/null +++ b/v2/io/functor.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "github.com/IBM/fp-go/v2/internal/functor" + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Functor returns the monadic operations for [IO] +func Functor[A, B any]() functor.Functor[A, B, IO[A], IO[B]] { + return G.Functor[A, B, IO[A], IO[B]]() +} diff --git a/v2/io/gen.go b/v2/io/gen.go new file mode 100644 index 0000000..269cc32 --- /dev/null +++ b/v2/io/gen.go @@ -0,0 +1,375 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:10.2607969 +0100 CET m=+0.024787801 + +package io + +import ( + G "github.com/IBM/fp-go/v2/io/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT1 converts 1 [IO[T]] into a [IO[T.Tuple1[T1]]] +func SequenceT1[T1 any]( + t1 IO[T1], +) IO[T.Tuple1[T1]] { + return G.SequenceT1[ + IO[T.Tuple1[T1]], + IO[T1], + ](t1) +} + +// SequenceTuple1 converts a [T.Tuple1[IO[T]]] into a [IO[T.Tuple1[T1]]] +func SequenceTuple1[T1 any](t T.Tuple1[IO[T1]]) IO[T.Tuple1[T1]] { + return G.SequenceTuple1[ + IO[T.Tuple1[T1]], + IO[T1], + ](t) +} + +// TraverseTuple1 converts a [T.Tuple1[IO[T]]] into a [IO[T.Tuple1[T1]]] +func TraverseTuple1[F1 ~func(A1) IO[T1], A1, T1 any](f1 F1) func(T.Tuple1[A1]) IO[T.Tuple1[T1]] { + return G.TraverseTuple1[IO[T.Tuple1[T1]]](f1) +} + +// SequenceT2 converts 2 [IO[T]] into a [IO[T.Tuple2[T1, T2]]] +func SequenceT2[T1, T2 any]( + t1 IO[T1], + t2 IO[T2], +) IO[T.Tuple2[T1, T2]] { + return G.SequenceT2[ + IO[T.Tuple2[T1, T2]], + IO[T1], + IO[T2], + ](t1, t2) +} + +// SequenceTuple2 converts a [T.Tuple2[IO[T]]] into a [IO[T.Tuple2[T1, T2]]] +func SequenceTuple2[T1, T2 any](t T.Tuple2[IO[T1], IO[T2]]) IO[T.Tuple2[T1, T2]] { + return G.SequenceTuple2[ + IO[T.Tuple2[T1, T2]], + IO[T1], + IO[T2], + ](t) +} + +// TraverseTuple2 converts a [T.Tuple2[IO[T]]] into a [IO[T.Tuple2[T1, T2]]] +func TraverseTuple2[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], A1, A2, T1, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) IO[T.Tuple2[T1, T2]] { + return G.TraverseTuple2[IO[T.Tuple2[T1, T2]]](f1, f2) +} + +// SequenceT3 converts 3 [IO[T]] into a [IO[T.Tuple3[T1, T2, T3]]] +func SequenceT3[T1, T2, T3 any]( + t1 IO[T1], + t2 IO[T2], + t3 IO[T3], +) IO[T.Tuple3[T1, T2, T3]] { + return G.SequenceT3[ + IO[T.Tuple3[T1, T2, T3]], + IO[T1], + IO[T2], + IO[T3], + ](t1, t2, t3) +} + +// SequenceTuple3 converts a [T.Tuple3[IO[T]]] into a [IO[T.Tuple3[T1, T2, T3]]] +func SequenceTuple3[T1, T2, T3 any](t T.Tuple3[IO[T1], IO[T2], IO[T3]]) IO[T.Tuple3[T1, T2, T3]] { + return G.SequenceTuple3[ + IO[T.Tuple3[T1, T2, T3]], + IO[T1], + IO[T2], + IO[T3], + ](t) +} + +// TraverseTuple3 converts a [T.Tuple3[IO[T]]] into a [IO[T.Tuple3[T1, T2, T3]]] +func TraverseTuple3[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], F3 ~func(A3) IO[T3], A1, A2, A3, T1, T2, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) IO[T.Tuple3[T1, T2, T3]] { + return G.TraverseTuple3[IO[T.Tuple3[T1, T2, T3]]](f1, f2, f3) +} + +// SequenceT4 converts 4 [IO[T]] into a [IO[T.Tuple4[T1, T2, T3, T4]]] +func SequenceT4[T1, T2, T3, T4 any]( + t1 IO[T1], + t2 IO[T2], + t3 IO[T3], + t4 IO[T4], +) IO[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceT4[ + IO[T.Tuple4[T1, T2, T3, T4]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + ](t1, t2, t3, t4) +} + +// SequenceTuple4 converts a [T.Tuple4[IO[T]]] into a [IO[T.Tuple4[T1, T2, T3, T4]]] +func SequenceTuple4[T1, T2, T3, T4 any](t T.Tuple4[IO[T1], IO[T2], IO[T3], IO[T4]]) IO[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceTuple4[ + IO[T.Tuple4[T1, T2, T3, T4]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + ](t) +} + +// TraverseTuple4 converts a [T.Tuple4[IO[T]]] into a [IO[T.Tuple4[T1, T2, T3, T4]]] +func TraverseTuple4[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], F3 ~func(A3) IO[T3], F4 ~func(A4) IO[T4], A1, A2, A3, A4, T1, T2, T3, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) IO[T.Tuple4[T1, T2, T3, T4]] { + return G.TraverseTuple4[IO[T.Tuple4[T1, T2, T3, T4]]](f1, f2, f3, f4) +} + +// SequenceT5 converts 5 [IO[T]] into a [IO[T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceT5[T1, T2, T3, T4, T5 any]( + t1 IO[T1], + t2 IO[T2], + t3 IO[T3], + t4 IO[T4], + t5 IO[T5], +) IO[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceT5[ + IO[T.Tuple5[T1, T2, T3, T4, T5]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + ](t1, t2, t3, t4, t5) +} + +// SequenceTuple5 converts a [T.Tuple5[IO[T]]] into a [IO[T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceTuple5[T1, T2, T3, T4, T5 any](t T.Tuple5[IO[T1], IO[T2], IO[T3], IO[T4], IO[T5]]) IO[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceTuple5[ + IO[T.Tuple5[T1, T2, T3, T4, T5]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + ](t) +} + +// TraverseTuple5 converts a [T.Tuple5[IO[T]]] into a [IO[T.Tuple5[T1, T2, T3, T4, T5]]] +func TraverseTuple5[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], F3 ~func(A3) IO[T3], F4 ~func(A4) IO[T4], F5 ~func(A5) IO[T5], A1, A2, A3, A4, A5, T1, T2, T3, T4, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) IO[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.TraverseTuple5[IO[T.Tuple5[T1, T2, T3, T4, T5]]](f1, f2, f3, f4, f5) +} + +// SequenceT6 converts 6 [IO[T]] into a [IO[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceT6[T1, T2, T3, T4, T5, T6 any]( + t1 IO[T1], + t2 IO[T2], + t3 IO[T3], + t4 IO[T4], + t5 IO[T5], + t6 IO[T6], +) IO[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceT6[ + IO[T.Tuple6[T1, T2, T3, T4, T5, T6]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + ](t1, t2, t3, t4, t5, t6) +} + +// SequenceTuple6 converts a [T.Tuple6[IO[T]]] into a [IO[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceTuple6[T1, T2, T3, T4, T5, T6 any](t T.Tuple6[IO[T1], IO[T2], IO[T3], IO[T4], IO[T5], IO[T6]]) IO[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceTuple6[ + IO[T.Tuple6[T1, T2, T3, T4, T5, T6]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + ](t) +} + +// TraverseTuple6 converts a [T.Tuple6[IO[T]]] into a [IO[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func TraverseTuple6[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], F3 ~func(A3) IO[T3], F4 ~func(A4) IO[T4], F5 ~func(A5) IO[T5], F6 ~func(A6) IO[T6], A1, A2, A3, A4, A5, A6, T1, T2, T3, T4, T5, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) IO[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.TraverseTuple6[IO[T.Tuple6[T1, T2, T3, T4, T5, T6]]](f1, f2, f3, f4, f5, f6) +} + +// SequenceT7 converts 7 [IO[T]] into a [IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceT7[T1, T2, T3, T4, T5, T6, T7 any]( + t1 IO[T1], + t2 IO[T2], + t3 IO[T3], + t4 IO[T4], + t5 IO[T5], + t6 IO[T6], + t7 IO[T7], +) IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceT7[ + IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + IO[T7], + ](t1, t2, t3, t4, t5, t6, t7) +} + +// SequenceTuple7 converts a [T.Tuple7[IO[T]]] into a [IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceTuple7[T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[IO[T1], IO[T2], IO[T3], IO[T4], IO[T5], IO[T6], IO[T7]]) IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceTuple7[ + IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + IO[T7], + ](t) +} + +// TraverseTuple7 converts a [T.Tuple7[IO[T]]] into a [IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func TraverseTuple7[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], F3 ~func(A3) IO[T3], F4 ~func(A4) IO[T4], F5 ~func(A5) IO[T5], F6 ~func(A6) IO[T6], F7 ~func(A7) IO[T7], A1, A2, A3, A4, A5, A6, A7, T1, T2, T3, T4, T5, T6, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.TraverseTuple7[IO[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](f1, f2, f3, f4, f5, f6, f7) +} + +// SequenceT8 converts 8 [IO[T]] into a [IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceT8[T1, T2, T3, T4, T5, T6, T7, T8 any]( + t1 IO[T1], + t2 IO[T2], + t3 IO[T3], + t4 IO[T4], + t5 IO[T5], + t6 IO[T6], + t7 IO[T7], + t8 IO[T8], +) IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceT8[ + IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + IO[T7], + IO[T8], + ](t1, t2, t3, t4, t5, t6, t7, t8) +} + +// SequenceTuple8 converts a [T.Tuple8[IO[T]]] into a [IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[IO[T1], IO[T2], IO[T3], IO[T4], IO[T5], IO[T6], IO[T7], IO[T8]]) IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceTuple8[ + IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + IO[T7], + IO[T8], + ](t) +} + +// TraverseTuple8 converts a [T.Tuple8[IO[T]]] into a [IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func TraverseTuple8[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], F3 ~func(A3) IO[T3], F4 ~func(A4) IO[T4], F5 ~func(A5) IO[T5], F6 ~func(A6) IO[T6], F7 ~func(A7) IO[T7], F8 ~func(A8) IO[T8], A1, A2, A3, A4, A5, A6, A7, A8, T1, T2, T3, T4, T5, T6, T7, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.TraverseTuple8[IO[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](f1, f2, f3, f4, f5, f6, f7, f8) +} + +// SequenceT9 converts 9 [IO[T]] into a [IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any]( + t1 IO[T1], + t2 IO[T2], + t3 IO[T3], + t4 IO[T4], + t5 IO[T5], + t6 IO[T6], + t7 IO[T7], + t8 IO[T8], + t9 IO[T9], +) IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceT9[ + IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + IO[T7], + IO[T8], + IO[T9], + ](t1, t2, t3, t4, t5, t6, t7, t8, t9) +} + +// SequenceTuple9 converts a [T.Tuple9[IO[T]]] into a [IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[IO[T1], IO[T2], IO[T3], IO[T4], IO[T5], IO[T6], IO[T7], IO[T8], IO[T9]]) IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceTuple9[ + IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + IO[T7], + IO[T8], + IO[T9], + ](t) +} + +// TraverseTuple9 converts a [T.Tuple9[IO[T]]] into a [IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func TraverseTuple9[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], F3 ~func(A3) IO[T3], F4 ~func(A4) IO[T4], F5 ~func(A5) IO[T5], F6 ~func(A6) IO[T6], F7 ~func(A7) IO[T7], F8 ~func(A8) IO[T8], F9 ~func(A9) IO[T9], A1, A2, A3, A4, A5, A6, A7, A8, A9, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.TraverseTuple9[IO[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](f1, f2, f3, f4, f5, f6, f7, f8, f9) +} + +// SequenceT10 converts 10 [IO[T]] into a [IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any]( + t1 IO[T1], + t2 IO[T2], + t3 IO[T3], + t4 IO[T4], + t5 IO[T5], + t6 IO[T6], + t7 IO[T7], + t8 IO[T8], + t9 IO[T9], + t10 IO[T10], +) IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceT10[ + IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + IO[T7], + IO[T8], + IO[T9], + IO[T10], + ](t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) +} + +// SequenceTuple10 converts a [T.Tuple10[IO[T]]] into a [IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[IO[T1], IO[T2], IO[T3], IO[T4], IO[T5], IO[T6], IO[T7], IO[T8], IO[T9], IO[T10]]) IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceTuple10[ + IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + IO[T1], + IO[T2], + IO[T3], + IO[T4], + IO[T5], + IO[T6], + IO[T7], + IO[T8], + IO[T9], + IO[T10], + ](t) +} + +// TraverseTuple10 converts a [T.Tuple10[IO[T]]] into a [IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func TraverseTuple10[F1 ~func(A1) IO[T1], F2 ~func(A2) IO[T2], F3 ~func(A3) IO[T3], F4 ~func(A4) IO[T4], F5 ~func(A5) IO[T5], F6 ~func(A6) IO[T6], F7 ~func(A7) IO[T7], F8 ~func(A8) IO[T8], F9 ~func(A9) IO[T9], F10 ~func(A10) IO[T10], A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.TraverseTuple10[IO[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) +} diff --git a/v2/io/generic/ap.go b/v2/io/generic/ap.go new file mode 100644 index 0000000..bcf0a70 --- /dev/null +++ b/v2/io/generic/ap.go @@ -0,0 +1,137 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + G "github.com/IBM/fp-go/v2/internal/apply" +) + +const ( + // useParallel is the feature flag to control if we use the parallel or the sequential implementation of ap + useParallel = true +) + +// MonadApSeq implements the applicative on a single thread by first executing mab and the ma +func MonadApSeq[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB { + return MonadChain(mab, F.Bind1st(MonadMap[GA, GB], ma)) +} + +// MonadApPar implements the applicative on two threads, the main thread executes mab and the actuall +// apply operation and the second thread computes ma. Communication between the threads happens via a channel +func MonadApPar[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB { + return MakeIO[GB](func() B { + c := make(chan A) + go func() { + c <- ma() + close(c) + }() + return mab()(<-c) + }) +} + +// MonadAp implements the `ap` operation. Depending on a feature flag this will be sequential or parallel, the preferred implementation +// is parallel +func MonadAp[GA ~func() A, GB ~func() B, GAB ~func() func(A) B, A, B any](mab GAB, ma GA) GB { + if useParallel { + return MonadApPar[GA, GB](mab, ma) + } + return MonadApSeq[GA, GB](mab, ma) +} + +// MonadApFirst combines two effectful actions, keeping only the result of the first. +func MonadApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA { + return G.MonadApFirst( + MonadAp[GB, GA, GBA, B, A], + MonadMap[GA, GBA, A, func(B) A], + + first, + second, + ) +} + +// MonadApFirstPar combines two effectful actions, keeping only the result of the first. +func MonadApFirstPar[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA { + return G.MonadApFirst( + MonadApPar[GB, GA, GBA, B, A], + MonadMap[GA, GBA, A, func(B) A], + + first, + second, + ) +} + +// MonadApFirstSeq combines two effectful actions, keeping only the result of the first. +func MonadApFirstSeq[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](first GA, second GB) GA { + return G.MonadApFirst( + MonadApSeq[GB, GA, GBA, B, A], + MonadMap[GA, GBA, A, func(B) A], + + first, + second, + ) +} + +// ApFirst combines two effectful actions, keeping only the result of the first. +func ApFirst[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA { + return G.ApFirst( + MonadAp[GB, GA, GBA, B, A], + MonadMap[GA, GBA, A, func(B) A], + + second, + ) +} + +// ApFirstPar combines two effectful actions, keeping only the result of the first. +func ApFirstPar[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA { + return G.ApFirst( + MonadApPar[GB, GA, GBA, B, A], + MonadMap[GA, GBA, A, func(B) A], + + second, + ) +} + +// ApFirstSeq combines two effectful actions, keeping only the result of the first. +func ApFirstSeq[GA ~func() A, GB ~func() B, GBA ~func() func(B) A, A, B any](second GB) func(GA) GA { + return G.ApFirst( + MonadApSeq[GB, GA, GBA, B, A], + MonadMap[GA, GBA, A, func(B) A], + + second, + ) +} + +// MonadApSecond combines two effectful actions, keeping only the result of the second. +func MonadApSecond[GA ~func() A, GB ~func() B, GBB ~func() func(B) B, A, B any](first GA, second GB) GB { + return G.MonadApSecond( + MonadAp[GB, GB, GBB, B, B], + MonadMap[GA, GBB, A, func(B) B], + + first, + second, + ) +} + +// ApSecond combines two effectful actions, keeping only the result of the second. +func ApSecond[GA ~func() A, GB ~func() B, GBB ~func() func(B) B, A, B any](second GB) func(GA) GB { + return G.ApSecond( + MonadAp[GB, GB, GBB, B, B], + MonadMap[GA, GBB, A, func(B) B], + + second, + ) +} diff --git a/v2/io/generic/apply.go b/v2/io/generic/apply.go new file mode 100644 index 0000000..331c05c --- /dev/null +++ b/v2/io/generic/apply.go @@ -0,0 +1,29 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func ApplySemigroup[GA ~func() A, A any](s S.Semigroup[A]) S.Semigroup[GA] { + return S.ApplySemigroup(MonadMap[GA, func() func(A) A, A, func(A) A], MonadAp[GA, GA, func() func(A) A, A, A], s) +} + +func ApplicativeMonoid[GA ~func() A, A any](m M.Monoid[A]) M.Monoid[GA] { + return M.ApplicativeMonoid(Of[GA, A], MonadMap[GA, func() func(A) A, A, func(A) A], MonadAp[GA, GA, func() func(A) A, A, A], m) +} diff --git a/v2/io/generic/bind.go b/v2/io/generic/bind.go new file mode 100644 index 0000000..9185436 --- /dev/null +++ b/v2/io/generic/bind.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GS ~func() S, S any]( + empty S, +) GS { + return Of[GS](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GT, +) func(GS1) GS2 { + return C.Bind( + Chain[GS1, GS2, S1, S2], + Map[GT, GS2, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GS1 ~func() S1, GS2 ~func() S2, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GS1) GS2 { + return F.Let( + Map[GS1, GS2, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GS1 ~func() S1, GS2 ~func() S2, S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GS1) GS2 { + return F.LetTo( + Map[GS1, GS2, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GS1 ~func() S1, GT ~func() T, S1, T any]( + setter func(T) S1, +) func(GT) GS1 { + return C.BindTo( + Map[GT, GS1, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, S1, S2, T any]( + setter func(T) func(S1) S2, + fa GT, +) func(GS1) GS2 { + return A.ApS( + Ap[GS2, func() func(T) S2, GT, S2, T], + Map[GS1, func() func(T) S2, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/io/generic/bracket.go b/v2/io/generic/bracket.go new file mode 100644 index 0000000..7015a57 --- /dev/null +++ b/v2/io/generic/bracket.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + G "github.com/IBM/fp-go/v2/internal/bracket" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[ + GA ~func() A, + GB ~func() B, + GANY ~func() ANY, + A, B, ANY any]( + + acquire GA, + use func(A) GB, + release func(A, B) GANY, +) GB { + return G.Bracket[GA, GB, GANY, B, A, B]( + Of[GB, B], + MonadChain[GA, GB, A, B], + MonadChain[GB, GB, B, B], + MonadChain[GANY, GB, ANY, B], + + acquire, + use, + release, + ) +} diff --git a/v2/io/generic/eq.go b/v2/io/generic/eq.go new file mode 100644 index 0000000..404da3a --- /dev/null +++ b/v2/io/generic/eq.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/internal/eq" +) + +// Eq implements the equals predicate for values contained in the IO monad +func Eq[GA ~func() A, A any](e EQ.Eq[A]) EQ.Eq[GA] { + // comparator for the monad + eq := G.Eq( + MonadMap[GA, func() func(A) bool, A, func(A) bool], + MonadAp[GA, func() bool, func() func(A) bool, A, bool], + e, + ) + // eagerly execute + return EQ.FromEquals(func(l, r GA) bool { + return eq(l, r)() + }) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[GA ~func() A, A comparable]() EQ.Eq[GA] { + return Eq[GA](EQ.FromStrictEquals[A]()) +} diff --git a/v2/io/generic/functor.go b/v2/io/generic/functor.go new file mode 100644 index 0000000..35840df --- /dev/null +++ b/v2/io/generic/functor.go @@ -0,0 +1,31 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/functor" +) + +type ioFunctor[A, B any, GA ~func() A, GB ~func() B] struct{} + +func (o *ioFunctor[A, B, GA, GB]) Map(f func(A) B) func(GA) GB { + return Map[GA, GB, A, B](f) +} + +// Functor implements the functoric operations for [IO] +func Functor[A, B any, GA ~func() A, GB ~func() B]() functor.Functor[A, B, GA, GB] { + return &ioFunctor[A, B, GA, GB]{} +} diff --git a/v2/io/generic/gen.go b/v2/io/generic/gen.go new file mode 100644 index 0000000..12c1b2d --- /dev/null +++ b/v2/io/generic/gen.go @@ -0,0 +1,829 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:10.2613281 +0100 CET m=+0.025319001 +package generic + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT1 converts 1 [func() T] into a [func() T.Tuple1[T1]] +func SequenceT1[ + G_TUPLE1 ~func() T.Tuple1[T1], + G_T1 ~func() T1, + T1 any]( + t1 G_T1, +) G_TUPLE1 { + return A.SequenceT1( + Map[G_T1, G_TUPLE1, T1, T.Tuple1[T1]], + t1, + ) +} + +// SequenceTuple1 converts a [T.Tuple1[func() T]] into a [func() T.Tuple1[T1]] +func SequenceTuple1[ + G_TUPLE1 ~func() T.Tuple1[T1], + G_T1 ~func() T1, + T1 any](t T.Tuple1[G_T1]) G_TUPLE1 { + return A.SequenceTuple1( + Map[G_T1, G_TUPLE1, T1, T.Tuple1[T1]], + t) +} + +// TraverseTuple1 converts a [T.Tuple1[func() T]] into a [func() T.Tuple1[T1]] +func TraverseTuple1[ + G_TUPLE1 ~func() T.Tuple1[T1], + F1 ~func(A1) G_T1, + G_T1 ~func() T1, + A1, T1 any](f1 F1) func(T.Tuple1[A1]) G_TUPLE1 { + return func(t T.Tuple1[A1]) G_TUPLE1 { + return A.TraverseTuple1( + Map[G_T1, G_TUPLE1, T1, T.Tuple1[T1]], + f1, + t) + } +} + +// SequenceT2 converts 2 [func() T] into a [func() T.Tuple2[T1, T2]] +func SequenceT2[ + G_TUPLE2 ~func() T.Tuple2[T1, T2], + G_T1 ~func() T1, + G_T2 ~func() T2, + T1, T2 any]( + t1 G_T1, + t2 G_T2, +) G_TUPLE2 { + return A.SequenceT2( + Map[G_T1, func() func(T2) T.Tuple2[T1, T2], T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() func(T2) T.Tuple2[T1, T2], G_T2], + t1, + t2, + ) +} + +// SequenceTuple2 converts a [T.Tuple2[func() T]] into a [func() T.Tuple2[T1, T2]] +func SequenceTuple2[ + G_TUPLE2 ~func() T.Tuple2[T1, T2], + G_T1 ~func() T1, + G_T2 ~func() T2, + T1, T2 any](t T.Tuple2[G_T1, G_T2]) G_TUPLE2 { + return A.SequenceTuple2( + Map[G_T1, func() func(T2) T.Tuple2[T1, T2], T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() func(T2) T.Tuple2[T1, T2], G_T2], + t) +} + +// TraverseTuple2 converts a [T.Tuple2[func() T]] into a [func() T.Tuple2[T1, T2]] +func TraverseTuple2[ + G_TUPLE2 ~func() T.Tuple2[T1, T2], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + G_T1 ~func() T1, + G_T2 ~func() T2, + A1, A2, T1, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) G_TUPLE2 { + return func(t T.Tuple2[A1, A2]) G_TUPLE2 { + return A.TraverseTuple2( + Map[G_T1, func() func(T2) T.Tuple2[T1, T2], T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() func(T2) T.Tuple2[T1, T2], G_T2], + f1, + f2, + t) + } +} + +// SequenceT3 converts 3 [func() T] into a [func() T.Tuple3[T1, T2, T3]] +func SequenceT3[ + G_TUPLE3 ~func() T.Tuple3[T1, T2, T3], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + T1, T2, T3 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, +) G_TUPLE3 { + return A.SequenceT3( + Map[G_T1, func() func(T2) func(T3) T.Tuple3[T1, T2, T3], T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() func(T3) T.Tuple3[T1, T2, T3], func() func(T2) func(T3) T.Tuple3[T1, T2, T3], G_T2], + Ap[G_TUPLE3, func() func(T3) T.Tuple3[T1, T2, T3], G_T3], + t1, + t2, + t3, + ) +} + +// SequenceTuple3 converts a [T.Tuple3[func() T]] into a [func() T.Tuple3[T1, T2, T3]] +func SequenceTuple3[ + G_TUPLE3 ~func() T.Tuple3[T1, T2, T3], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + T1, T2, T3 any](t T.Tuple3[G_T1, G_T2, G_T3]) G_TUPLE3 { + return A.SequenceTuple3( + Map[G_T1, func() func(T2) func(T3) T.Tuple3[T1, T2, T3], T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() func(T3) T.Tuple3[T1, T2, T3], func() func(T2) func(T3) T.Tuple3[T1, T2, T3], G_T2], + Ap[G_TUPLE3, func() func(T3) T.Tuple3[T1, T2, T3], G_T3], + t) +} + +// TraverseTuple3 converts a [T.Tuple3[func() T]] into a [func() T.Tuple3[T1, T2, T3]] +func TraverseTuple3[ + G_TUPLE3 ~func() T.Tuple3[T1, T2, T3], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + A1, A2, A3, T1, T2, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) G_TUPLE3 { + return func(t T.Tuple3[A1, A2, A3]) G_TUPLE3 { + return A.TraverseTuple3( + Map[G_T1, func() func(T2) func(T3) T.Tuple3[T1, T2, T3], T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() func(T3) T.Tuple3[T1, T2, T3], func() func(T2) func(T3) T.Tuple3[T1, T2, T3], G_T2], + Ap[G_TUPLE3, func() func(T3) T.Tuple3[T1, T2, T3], G_T3], + f1, + f2, + f3, + t) + } +} + +// SequenceT4 converts 4 [func() T] into a [func() T.Tuple4[T1, T2, T3, T4]] +func SequenceT4[ + G_TUPLE4 ~func() T.Tuple4[T1, T2, T3, T4], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + T1, T2, T3, T4 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, +) G_TUPLE4 { + return A.SequenceT4( + Map[G_T1, func() func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], func() func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], G_T2], + Ap[func() func(T4) T.Tuple4[T1, T2, T3, T4], func() func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], G_T3], + Ap[G_TUPLE4, func() func(T4) T.Tuple4[T1, T2, T3, T4], G_T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceTuple4 converts a [T.Tuple4[func() T]] into a [func() T.Tuple4[T1, T2, T3, T4]] +func SequenceTuple4[ + G_TUPLE4 ~func() T.Tuple4[T1, T2, T3, T4], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + T1, T2, T3, T4 any](t T.Tuple4[G_T1, G_T2, G_T3, G_T4]) G_TUPLE4 { + return A.SequenceTuple4( + Map[G_T1, func() func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], func() func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], G_T2], + Ap[func() func(T4) T.Tuple4[T1, T2, T3, T4], func() func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], G_T3], + Ap[G_TUPLE4, func() func(T4) T.Tuple4[T1, T2, T3, T4], G_T4], + t) +} + +// TraverseTuple4 converts a [T.Tuple4[func() T]] into a [func() T.Tuple4[T1, T2, T3, T4]] +func TraverseTuple4[ + G_TUPLE4 ~func() T.Tuple4[T1, T2, T3, T4], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + A1, A2, A3, A4, T1, T2, T3, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) G_TUPLE4 { + return func(t T.Tuple4[A1, A2, A3, A4]) G_TUPLE4 { + return A.TraverseTuple4( + Map[G_T1, func() func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], func() func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], G_T2], + Ap[func() func(T4) T.Tuple4[T1, T2, T3, T4], func() func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], G_T3], + Ap[G_TUPLE4, func() func(T4) T.Tuple4[T1, T2, T3, T4], G_T4], + f1, + f2, + f3, + f4, + t) + } +} + +// SequenceT5 converts 5 [func() T] into a [func() T.Tuple5[T1, T2, T3, T4, T5]] +func SequenceT5[ + G_TUPLE5 ~func() T.Tuple5[T1, T2, T3, T4, T5], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + T1, T2, T3, T4, T5 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, +) G_TUPLE5 { + return A.SequenceT5( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T2], + Ap[func() func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T3], + Ap[func() func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T4], + Ap[G_TUPLE5, func() func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceTuple5 converts a [T.Tuple5[func() T]] into a [func() T.Tuple5[T1, T2, T3, T4, T5]] +func SequenceTuple5[ + G_TUPLE5 ~func() T.Tuple5[T1, T2, T3, T4, T5], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + T1, T2, T3, T4, T5 any](t T.Tuple5[G_T1, G_T2, G_T3, G_T4, G_T5]) G_TUPLE5 { + return A.SequenceTuple5( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T2], + Ap[func() func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T3], + Ap[func() func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T4], + Ap[G_TUPLE5, func() func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T5], + t) +} + +// TraverseTuple5 converts a [T.Tuple5[func() T]] into a [func() T.Tuple5[T1, T2, T3, T4, T5]] +func TraverseTuple5[ + G_TUPLE5 ~func() T.Tuple5[T1, T2, T3, T4, T5], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + A1, A2, A3, A4, A5, T1, T2, T3, T4, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) G_TUPLE5 { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) G_TUPLE5 { + return A.TraverseTuple5( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T2], + Ap[func() func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T3], + Ap[func() func(T5) T.Tuple5[T1, T2, T3, T4, T5], func() func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T4], + Ap[G_TUPLE5, func() func(T5) T.Tuple5[T1, T2, T3, T4, T5], G_T5], + f1, + f2, + f3, + f4, + f5, + t) + } +} + +// SequenceT6 converts 6 [func() T] into a [func() T.Tuple6[T1, T2, T3, T4, T5, T6]] +func SequenceT6[ + G_TUPLE6 ~func() T.Tuple6[T1, T2, T3, T4, T5, T6], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + T1, T2, T3, T4, T5, T6 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, +) G_TUPLE6 { + return A.SequenceT6( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T2], + Ap[func() func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T3], + Ap[func() func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T4], + Ap[func() func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T5], + Ap[G_TUPLE6, func() func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceTuple6 converts a [T.Tuple6[func() T]] into a [func() T.Tuple6[T1, T2, T3, T4, T5, T6]] +func SequenceTuple6[ + G_TUPLE6 ~func() T.Tuple6[T1, T2, T3, T4, T5, T6], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + T1, T2, T3, T4, T5, T6 any](t T.Tuple6[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6]) G_TUPLE6 { + return A.SequenceTuple6( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T2], + Ap[func() func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T3], + Ap[func() func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T4], + Ap[func() func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T5], + Ap[G_TUPLE6, func() func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T6], + t) +} + +// TraverseTuple6 converts a [T.Tuple6[func() T]] into a [func() T.Tuple6[T1, T2, T3, T4, T5, T6]] +func TraverseTuple6[ + G_TUPLE6 ~func() T.Tuple6[T1, T2, T3, T4, T5, T6], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + A1, A2, A3, A4, A5, A6, T1, T2, T3, T4, T5, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) G_TUPLE6 { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) G_TUPLE6 { + return A.TraverseTuple6( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T2], + Ap[func() func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T3], + Ap[func() func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T4], + Ap[func() func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], func() func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T5], + Ap[G_TUPLE6, func() func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], G_T6], + f1, + f2, + f3, + f4, + f5, + f6, + t) + } +} + +// SequenceT7 converts 7 [func() T] into a [func() T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] +func SequenceT7[ + G_TUPLE7 ~func() T.Tuple7[T1, T2, T3, T4, T5, T6, T7], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + T1, T2, T3, T4, T5, T6, T7 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, +) G_TUPLE7 { + return A.SequenceT7( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T3], + Ap[func() func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T4], + Ap[func() func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T5], + Ap[func() func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T6], + Ap[G_TUPLE7, func() func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceTuple7 converts a [T.Tuple7[func() T]] into a [func() T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] +func SequenceTuple7[ + G_TUPLE7 ~func() T.Tuple7[T1, T2, T3, T4, T5, T6, T7], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7]) G_TUPLE7 { + return A.SequenceTuple7( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T3], + Ap[func() func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T4], + Ap[func() func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T5], + Ap[func() func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T6], + Ap[G_TUPLE7, func() func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T7], + t) +} + +// TraverseTuple7 converts a [T.Tuple7[func() T]] into a [func() T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] +func TraverseTuple7[ + G_TUPLE7 ~func() T.Tuple7[T1, T2, T3, T4, T5, T6, T7], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + A1, A2, A3, A4, A5, A6, A7, T1, T2, T3, T4, T5, T6, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) G_TUPLE7 { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) G_TUPLE7 { + return A.TraverseTuple7( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T3], + Ap[func() func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T4], + Ap[func() func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T5], + Ap[func() func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], func() func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T6], + Ap[G_TUPLE7, func() func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], G_T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t) + } +} + +// SequenceT8 converts 8 [func() T] into a [func() T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] +func SequenceT8[ + G_TUPLE8 ~func() T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + T1, T2, T3, T4, T5, T6, T7, T8 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, +) G_TUPLE8 { + return A.SequenceT8( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T4], + Ap[func() func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T5], + Ap[func() func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T6], + Ap[func() func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T7], + Ap[G_TUPLE8, func() func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceTuple8 converts a [T.Tuple8[func() T]] into a [func() T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] +func SequenceTuple8[ + G_TUPLE8 ~func() T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8]) G_TUPLE8 { + return A.SequenceTuple8( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T4], + Ap[func() func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T5], + Ap[func() func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T6], + Ap[func() func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T7], + Ap[G_TUPLE8, func() func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T8], + t) +} + +// TraverseTuple8 converts a [T.Tuple8[func() T]] into a [func() T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] +func TraverseTuple8[ + G_TUPLE8 ~func() T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + A1, A2, A3, A4, A5, A6, A7, A8, T1, T2, T3, T4, T5, T6, T7, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) G_TUPLE8 { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) G_TUPLE8 { + return A.TraverseTuple8( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T4], + Ap[func() func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T5], + Ap[func() func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T6], + Ap[func() func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], func() func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T7], + Ap[G_TUPLE8, func() func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], G_T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t) + } +} + +// SequenceT9 converts 9 [func() T] into a [func() T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] +func SequenceT9[ + G_TUPLE9 ~func() T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + G_T9 ~func() T9, + T1, T2, T3, T4, T5, T6, T7, T8, T9 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, + t9 G_T9, +) G_TUPLE9 { + return A.SequenceT9( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T4], + Ap[func() func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T5], + Ap[func() func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T6], + Ap[func() func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T7], + Ap[func() func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T8], + Ap[G_TUPLE9, func() func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceTuple9 converts a [T.Tuple9[func() T]] into a [func() T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] +func SequenceTuple9[ + G_TUPLE9 ~func() T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + G_T9 ~func() T9, + T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8, G_T9]) G_TUPLE9 { + return A.SequenceTuple9( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T4], + Ap[func() func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T5], + Ap[func() func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T6], + Ap[func() func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T7], + Ap[func() func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T8], + Ap[G_TUPLE9, func() func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T9], + t) +} + +// TraverseTuple9 converts a [T.Tuple9[func() T]] into a [func() T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] +func TraverseTuple9[ + G_TUPLE9 ~func() T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + F9 ~func(A9) G_T9, + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + G_T9 ~func() T9, + A1, A2, A3, A4, A5, A6, A7, A8, A9, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) G_TUPLE9 { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) G_TUPLE9 { + return A.TraverseTuple9( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T4], + Ap[func() func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T5], + Ap[func() func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T6], + Ap[func() func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T7], + Ap[func() func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], func() func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T8], + Ap[G_TUPLE9, func() func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], G_T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t) + } +} + +// SequenceT10 converts 10 [func() T] into a [func() T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] +func SequenceT10[ + G_TUPLE10 ~func() T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + G_T9 ~func() T9, + G_T10 ~func() T10, + T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, + t9 G_T9, + t10 G_T10, +) G_TUPLE10 { + return A.SequenceT10( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T4], + Ap[func() func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T5], + Ap[func() func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T6], + Ap[func() func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T7], + Ap[func() func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T8], + Ap[func() func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T9], + Ap[G_TUPLE10, func() func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceTuple10 converts a [T.Tuple10[func() T]] into a [func() T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] +func SequenceTuple10[ + G_TUPLE10 ~func() T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + G_T9 ~func() T9, + G_T10 ~func() T10, + T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8, G_T9, G_T10]) G_TUPLE10 { + return A.SequenceTuple10( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T4], + Ap[func() func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T5], + Ap[func() func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T6], + Ap[func() func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T7], + Ap[func() func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T8], + Ap[func() func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T9], + Ap[G_TUPLE10, func() func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T10], + t) +} + +// TraverseTuple10 converts a [T.Tuple10[func() T]] into a [func() T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] +func TraverseTuple10[ + G_TUPLE10 ~func() T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + F9 ~func(A9) G_T9, + F10 ~func(A10) G_T10, + G_T1 ~func() T1, + G_T2 ~func() T2, + G_T3 ~func() T3, + G_T4 ~func() T4, + G_T5 ~func() T5, + G_T6 ~func() T6, + G_T7 ~func() T7, + G_T8 ~func() T8, + G_T9 ~func() T9, + G_T10 ~func() T10, + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) G_TUPLE10 { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) G_TUPLE10 { + return A.TraverseTuple10( + Map[G_T1, func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T2], + Ap[func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T3], + Ap[func() func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T4], + Ap[func() func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T5], + Ap[func() func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T6], + Ap[func() func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T7], + Ap[func() func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T8], + Ap[func() func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], func() func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T9], + Ap[G_TUPLE10, func() func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], G_T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t) + } +} diff --git a/v2/io/generic/io.go b/v2/io/generic/io.go new file mode 100644 index 0000000..2132ae0 --- /dev/null +++ b/v2/io/generic/io.go @@ -0,0 +1,207 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "time" + + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + FC "github.com/IBM/fp-go/v2/internal/functor" + L "github.com/IBM/fp-go/v2/internal/lazy" + T "github.com/IBM/fp-go/v2/tuple" +) + +var ( + // undefined represents an undefined value + undefined = struct{}{} +) + +// type IO[A any] = func() A + +func MakeIO[GA ~func() A, A any](f func() A) GA { + return f +} + +func Of[GA ~func() A, A any](a A) GA { + return MakeIO[GA](F.Constant(a)) +} + +func FromIO[GA ~func() A, A any](a GA) GA { + return a +} + +// FromImpure converts a side effect without a return value into a side effect that returns any +func FromImpure[GA ~func() any, IMP ~func()](f IMP) GA { + return MakeIO[GA](func() any { + f() + return undefined + }) +} + +func MonadOf[GA ~func() A, A any](a A) GA { + return MakeIO[GA](F.Constant(a)) +} + +func MonadMap[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) B) GB { + return MakeIO[GB](func() B { + return f(fa()) + }) +} + +func Map[GA ~func() A, GB ~func() B, A, B any](f func(A) B) func(GA) GB { + return F.Bind2nd(MonadMap[GA, GB, A, B], f) +} + +func MonadMapTo[GA ~func() A, GB ~func() B, A, B any](fa GA, b B) GB { + return MonadMap[GA, GB](fa, F.Constant1[A](b)) +} + +func MapTo[GA ~func() A, GB ~func() B, A, B any](b B) func(GA) GB { + return Map[GA, GB](F.Constant1[A](b)) +} + +// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation. +func MonadChain[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) GB) GB { + return MakeIO[GB](func() B { + return f(fa())() + }) +} + +// Chain composes computations in sequence, using the return value of one computation to determine the next computation. +func Chain[GA ~func() A, GB ~func() B, A, B any](f func(A) GB) func(GA) GB { + return F.Bind2nd(MonadChain[GA, GB, A, B], f) +} + +// MonadChainTo composes computations in sequence, ignoring the return value of the first computation +func MonadChainTo[GA ~func() A, GB ~func() B, A, B any](fa GA, fb GB) GB { + return MonadChain(fa, F.Constant1[A](fb)) +} + +// ChainTo composes computations in sequence, ignoring the return value of the first computation +func ChainTo[GA ~func() A, GB ~func() B, A, B any](fb GB) func(GA) GB { + return Chain[GA, GB](F.Constant1[A](fb)) +} + +// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and +// keeping only the result of the first. +func MonadChainFirst[GA ~func() A, GB ~func() B, A, B any](fa GA, f func(A) GB) GA { + return C.MonadChainFirst(MonadChain[GA, GA, A, A], MonadMap[GB, GA, B, A], fa, f) +} + +// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and +// keeping only the result of the first. +func ChainFirst[GA ~func() A, GB ~func() B, A, B any](f func(A) GB) func(GA) GA { + return C.ChainFirst( + Chain[GA, GA, A, A], + Map[GB, GA, B, A], + f, + ) +} + +func ApSeq[GB ~func() B, GAB ~func() func(A) B, GA ~func() A, B, A any](ma GA) func(GAB) GB { + return F.Bind2nd(MonadApSeq[GA, GB, GAB, A, B], ma) +} + +func ApPar[GB ~func() B, GAB ~func() func(A) B, GA ~func() A, B, A any](ma GA) func(GAB) GB { + return F.Bind2nd(MonadApPar[GA, GB, GAB, A, B], ma) +} + +func Ap[GB ~func() B, GAB ~func() func(A) B, GA ~func() A, B, A any](ma GA) func(GAB) GB { + return F.Bind2nd(MonadAp[GA, GB, GAB, A, B], ma) +} + +func Flatten[GA ~func() A, GAA ~func() GA, A any](mma GAA) GA { + return mma() +} + +// Memoize computes the value of the provided IO monad lazily but exactly once +func Memoize[GA ~func() A, A any](ma GA) GA { + return L.Memoize[GA, A](ma) +} + +// Delay creates an operation that passes in the value after some delay +func Delay[GA ~func() A, A any](delay time.Duration) func(GA) GA { + return func(ga GA) GA { + return MakeIO[GA](func() A { + time.Sleep(delay) + return ga() + }) + } +} + +func after(timestamp time.Time) func() { + return func() { + // check if we need to wait + current := time.Now() + if current.Before(timestamp) { + time.Sleep(timestamp.Sub(current)) + } + } +} + +// After creates an operation that passes after the given timestamp +func After[GA ~func() A, A any](timestamp time.Time) func(GA) GA { + aft := after(timestamp) + return func(ga GA) GA { + return MakeIO[GA](func() A { + // wait as long as necessary + aft() + // execute after wait + return ga() + }) + } +} + +// Now returns the current timestamp +func Now[GA ~func() time.Time]() GA { + return MakeIO[GA](time.Now) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[GA ~func() A, A any](gen func() GA) GA { + return MakeIO[GA](func() A { + return gen()() + }) +} + +func MonadFlap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](fab GFAB, a A) GB { + return FC.MonadFlap(MonadMap[GFAB, GB, FAB, B], fab, a) +} + +func Flap[FAB ~func(A) B, GFAB ~func() FAB, GB ~func() B, A, B any](a A) func(GFAB) GB { + return FC.Flap(Map[GFAB, GB, FAB, B], a) +} + +// WithTime returns an operation that measures the start and end timestamp of the operation +func WithTime[GTA ~func() T.Tuple3[A, time.Time, time.Time], GA ~func() A, A any](a GA) GTA { + return MakeIO[GTA](func() T.Tuple3[A, time.Time, time.Time] { + t0 := time.Now() + res := a() + t1 := time.Now() + return T.MakeTuple3(res, t0, t1) + }) +} + +// WithDuration returns an operation that measures the duration of the operation +func WithDuration[GTA ~func() T.Tuple2[A, time.Duration], GA ~func() A, A any](a GA) GTA { + return MakeIO[GTA](func() T.Tuple2[A, time.Duration] { + t0 := time.Now() + res := a() + t1 := time.Now() + return T.MakeTuple2(res, t1.Sub(t0)) + }) +} diff --git a/v2/io/generic/logging.go b/v2/io/generic/logging.go new file mode 100644 index 0000000..4747a1d --- /dev/null +++ b/v2/io/generic/logging.go @@ -0,0 +1,50 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "fmt" + "log" + + Logging "github.com/IBM/fp-go/v2/logging" +) + +func Logger[GA ~func() any, A any](loggers ...*log.Logger) func(string) func(A) GA { + _, right := Logging.LoggingCallbacks(loggers...) + return func(prefix string) func(A) GA { + return func(a A) GA { + return FromImpure[GA](func() { + right("%s: %v", prefix, a) + }) + } + } +} + +func Logf[GA ~func() any, A any](prefix string) func(A) GA { + return func(a A) GA { + return FromImpure[GA](func() { + log.Printf(prefix, a) + }) + } +} + +func Printf[GA ~func() any, A any](prefix string) func(A) GA { + return func(a A) GA { + return FromImpure[GA](func() { + fmt.Printf(prefix, a) + }) + } +} diff --git a/v2/io/generic/monad.go b/v2/io/generic/monad.go new file mode 100644 index 0000000..41ff613 --- /dev/null +++ b/v2/io/generic/monad.go @@ -0,0 +1,43 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/monad" +) + +type ioMonad[A, B any, GA ~func() A, GB ~func() B, GAB ~func() func(A) B] struct{} + +func (o *ioMonad[A, B, GA, GB, GAB]) Of(a A) GA { + return Of[GA, A](a) +} + +func (o *ioMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB { + return Map[GA, GB, A, B](f) +} + +func (o *ioMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB { + return Chain[GA, GB, A, B](f) +} + +func (o *ioMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB { + return Ap[GB, GAB, GA, B, A](fa) +} + +// Monad implements the monadic operations for [Option] +func Monad[A, B any, GA ~func() A, GB ~func() B, GAB ~func() func(A) B]() monad.Monad[A, B, GA, GB, GAB] { + return &ioMonad[A, B, GA, GB, GAB]{} +} diff --git a/v2/io/generic/pointed.go b/v2/io/generic/pointed.go new file mode 100644 index 0000000..79032e4 --- /dev/null +++ b/v2/io/generic/pointed.go @@ -0,0 +1,31 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/pointed" +) + +type ioPointed[A any, GA ~func() A] struct{} + +func (o *ioPointed[A, GA]) Of(a A) GA { + return Of[GA, A](a) +} + +// Pointed implements the pointedic operations for [IO] +func Pointed[A any, GA ~func() A]() pointed.Pointed[A, GA] { + return &ioPointed[A, GA]{} +} diff --git a/v2/io/generic/resource.go b/v2/io/generic/resource.go new file mode 100644 index 0000000..dab2307 --- /dev/null +++ b/v2/io/generic/resource.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[ + GA ~func() A, + GR ~func() R, + GANY ~func() ANY, + R, A, ANY any](onCreate GR, onRelease func(R) GANY) func(func(R) GA) GA { + // simply map to implementation of bracket + return F.Bind13of3(Bracket[GR, GA, GANY, R, A, ANY])(onCreate, F.Ignore2of2[A](onRelease)) +} diff --git a/v2/io/generic/retry.go b/v2/io/generic/retry.go new file mode 100644 index 0000000..d46bf37 --- /dev/null +++ b/v2/io/generic/retry.go @@ -0,0 +1,49 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + R "github.com/IBM/fp-go/v2/retry" + G "github.com/IBM/fp-go/v2/retry/generic" +) + +type retryStatusIO = func() R.RetryStatus + +// Retry combinator for actions that don't raise exceptions, but +// signal in their type the outcome has failed. Examples are the +// `Option`, `Either` and `EitherT` monads. +// +// policy - refers to the retry policy +// action - converts a status into an operation to be executed +// check - checks if the result of the action needs to be retried +func Retrying[GA ~func() A, A any]( + policy R.RetryPolicy, + action func(R.RetryStatus) GA, + check func(A) bool, +) GA { + // get an implementation for the types + return G.Retrying( + Chain[GA, GA, A, A], + Chain[retryStatusIO, GA, R.RetryStatus, A], + Of[GA, A], + Of[retryStatusIO, R.RetryStatus], + Delay[retryStatusIO, R.RetryStatus], + + policy, + action, + check, + ) +} diff --git a/v2/io/generic/sync.go b/v2/io/generic/sync.go new file mode 100644 index 0000000..f9811b2 --- /dev/null +++ b/v2/io/generic/sync.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[GA ~func() A, A any](lock func() context.CancelFunc) func(fa GA) GA { + return func(fa GA) GA { + return func() A { + defer lock()() + return fa() + } + } +} diff --git a/v2/io/generic/traverse.go b/v2/io/generic/traverse.go new file mode 100644 index 0000000..0b87d92 --- /dev/null +++ b/v2/io/generic/traverse.go @@ -0,0 +1,229 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" + RR "github.com/IBM/fp-go/v2/internal/record" +) + +func MonadTraverseArray[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](tas AAS, f func(A) GB) GBS { + return RA.MonadTraverse( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + Ap[GBS, func() func(B) BBS, GB], + + tas, + f, + ) +} + +func MonadTraverseArraySeq[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](tas AAS, f func(A) GB) GBS { + return RA.MonadTraverse( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + ApSeq[GBS, func() func(B) BBS, GB], + + tas, + f, + ) +} + +func MonadTraverseArrayPar[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](tas AAS, f func(A) GB) GBS { + return RA.MonadTraverse( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + ApPar[GBS, func() func(B) BBS, GB], + + tas, + f, + ) +} + +func TraverseArray[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + Ap[GBS, func() func(B) BBS, GB], + + f, + ) +} + +func TraverseArraySeq[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + ApSeq[GBS, func() func(B) BBS, GB], + + f, + ) +} + +func TraverseArrayPar[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + ApPar[GBS, func() func(B) BBS, GB], + + f, + ) +} + +func TraverseArrayWithIndex[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + Ap[GBS, func() func(B) BBS, GB], + + f, + ) +} + +func TraverseArrayWithIndexSeq[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + ApSeq[GBS, func() func(B) BBS, GB], + + f, + ) +} + +func TraverseArrayWithIndexPar[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, BBS], + Map[GBS, func() func(B) BBS, BBS, func(B) BBS], + ApPar[GBS, func() func(B) BBS, GB], + + f, + ) +} + +func SequenceArray[GA ~func() A, GAS ~func() AAS, AAS ~[]A, GAAS ~[]GA, A any](tas GAAS) GAS { + return MonadTraverseArray[GA, GAS](tas, F.Identity[GA]) +} + +func SequenceArraySeq[GA ~func() A, GAS ~func() AAS, AAS ~[]A, GAAS ~[]GA, A any](tas GAAS) GAS { + return MonadTraverseArraySeq[GA, GAS](tas, F.Identity[GA]) +} + +func SequenceArrayPar[GA ~func() A, GAS ~func() AAS, AAS ~[]A, GAAS ~[]GA, A any](tas GAAS) GAS { + return MonadTraverseArrayPar[GA, GAS](tas, F.Identity[GA]) +} + +// MonadTraverseRecord transforms a record using an IO transform an IO of a record +func MonadTraverseRecord[GBS ~func() MB, MA ~map[K]A, GB ~func() B, MB ~map[K]B, K comparable, A, B any](ma MA, f func(A) GB) GBS { + return RR.MonadTraverse[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + Ap[GBS, func() func(B) MB, GB], + ma, f, + ) +} + +// TraverseRecord transforms a record using an IO transform an IO of a record +func TraverseRecord[GBS ~func() MB, MA ~map[K]A, GB ~func() B, MB ~map[K]B, K comparable, A, B any](f func(A) GB) func(MA) GBS { + return RR.Traverse[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + Ap[GBS, func() func(B) MB, GB], + f, + ) +} + +// TraverseRecordWithIndex transforms a record using an IO transform an IO of a record +func TraverseRecordWithIndex[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[K]B, K comparable, A, B any](f func(K, A) GB) func(MA) GBS { + return RR.TraverseWithIndex[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + Ap[GBS, func() func(B) MB, GB], + f, + ) +} + +func SequenceRecord[GA ~func() A, GAS ~func() AAS, AAS ~map[K]A, GAAS ~map[K]GA, K comparable, A any](tas GAAS) GAS { + return MonadTraverseRecord[GAS](tas, F.Identity[GA]) +} + +// MonadTraverseRecordSeq transforms a record using an IO transform an IO of a record +func MonadTraverseRecordSeq[GBS ~func() MB, MA ~map[K]A, GB ~func() B, MB ~map[K]B, K comparable, A, B any](ma MA, f func(A) GB) GBS { + return RR.MonadTraverse[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + ApSeq[GBS, func() func(B) MB, GB], + ma, f, + ) +} + +// TraverseRecordSeq transforms a record using an IO transform an IO of a record +func TraverseRecordSeq[GBS ~func() MB, MA ~map[K]A, GB ~func() B, MB ~map[K]B, K comparable, A, B any](f func(A) GB) func(MA) GBS { + return RR.Traverse[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + ApSeq[GBS, func() func(B) MB, GB], + f, + ) +} + +// TraverseRecordWithIndexSeq transforms a record using an IO transform an IO of a record +func TraverseRecordWithIndexSeq[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[K]B, K comparable, A, B any](f func(K, A) GB) func(MA) GBS { + return RR.TraverseWithIndex[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + ApSeq[GBS, func() func(B) MB, GB], + f, + ) +} + +func SequenceRecordSeq[GA ~func() A, GAS ~func() AAS, AAS ~map[K]A, GAAS ~map[K]GA, K comparable, A any](tas GAAS) GAS { + return MonadTraverseRecordSeq[GAS](tas, F.Identity[GA]) +} + +// MonadTraverseRecordPar transforms a record using an IO transform an IO of a record +func MonadTraverseRecordPar[GBS ~func() MB, MA ~map[K]A, GB ~func() B, MB ~map[K]B, K comparable, A, B any](ma MA, f func(A) GB) GBS { + return RR.MonadTraverse[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + ApPar[GBS, func() func(B) MB, GB], + ma, f, + ) +} + +// TraverseRecordPar transforms a record using an IO transform an IO of a record +func TraverseRecordPar[GBS ~func() MB, MA ~map[K]A, GB ~func() B, MB ~map[K]B, K comparable, A, B any](f func(A) GB) func(MA) GBS { + return RR.Traverse[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + ApPar[GBS, func() func(B) MB, GB], + f, + ) +} + +// TraverseRecordWithIndexPar transforms a record using an IO transform an IO of a record +func TraverseRecordWithIndexPar[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[K]B, K comparable, A, B any](f func(K, A) GB) func(MA) GBS { + return RR.TraverseWithIndex[MA]( + Of[GBS, MB], + Map[GBS, func() func(B) MB, MB, func(B) MB], + ApPar[GBS, func() func(B) MB, GB], + f, + ) +} + +func SequenceRecordPar[GA ~func() A, GAS ~func() AAS, AAS ~map[K]A, GAAS ~map[K]GA, K comparable, A any](tas GAAS) GAS { + return MonadTraverseRecordPar[GAS](tas, F.Identity[GA]) +} diff --git a/v2/io/io.go b/v2/io/io.go new file mode 100644 index 0000000..11ee478 --- /dev/null +++ b/v2/io/io.go @@ -0,0 +1,169 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "time" + + G "github.com/IBM/fp-go/v2/io/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// IO represents a synchronous computation that cannot fail +// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioltagt] for more details +type IO[A any] func() A + +func MakeIO[A any](f func() A) IO[A] { + return G.MakeIO[IO[A]](f) +} + +func Of[A any](a A) IO[A] { + return G.Of[IO[A]](a) +} + +func FromIO[A any](a IO[A]) IO[A] { + return G.FromIO(a) +} + +// FromImpure converts a side effect without a return value into a side effect that returns any +func FromImpure(f func()) IO[any] { + return G.FromImpure[IO[any]](f) +} + +func MonadOf[A any](a A) IO[A] { + return G.MonadOf[IO[A]](a) +} + +func MonadMap[A, B any](fa IO[A], f func(A) B) IO[B] { + return G.MonadMap[IO[A], IO[B]](fa, f) +} + +func Map[A, B any](f func(A) B) func(fa IO[A]) IO[B] { + return G.Map[IO[A], IO[B]](f) +} + +func MonadMapTo[A, B any](fa IO[A], b B) IO[B] { + return G.MonadMapTo[IO[A], IO[B]](fa, b) +} + +func MapTo[A, B any](b B) func(IO[A]) IO[B] { + return G.MapTo[IO[A], IO[B]](b) +} + +// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation. +func MonadChain[A, B any](fa IO[A], f func(A) IO[B]) IO[B] { + return G.MonadChain(fa, f) +} + +// Chain composes computations in sequence, using the return value of one computation to determine the next computation. +func Chain[A, B any](f func(A) IO[B]) func(IO[A]) IO[B] { + return G.Chain[IO[A]](f) +} + +func MonadAp[B, A any](mab IO[func(A) B], ma IO[A]) IO[B] { + return G.MonadAp[IO[A], IO[B]](mab, ma) +} + +func Ap[B, A any](ma IO[A]) func(IO[func(A) B]) IO[B] { + return G.Ap[IO[B], IO[func(A) B], IO[A]](ma) +} + +func Flatten[A any](mma IO[IO[A]]) IO[A] { + return G.Flatten(mma) +} + +// Memoize computes the value of the provided [IO] monad lazily but exactly once +func Memoize[A any](ma IO[A]) IO[A] { + return G.Memoize(ma) +} + +// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and +// keeping only the result of the first. +func MonadChainFirst[A, B any](fa IO[A], f func(A) IO[B]) IO[A] { + return G.MonadChainFirst(fa, f) +} + +// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and +// keeping only the result of the first. +func ChainFirst[A, B any](f func(A) IO[B]) func(IO[A]) IO[A] { + return G.ChainFirst[IO[A]](f) +} + +// MonadApFirst combines two effectful actions, keeping only the result of the first. +func MonadApFirst[A, B any](first IO[A], second IO[B]) IO[A] { + return G.MonadApFirst[IO[A], IO[B], IO[func(B) A]](first, second) +} + +// ApFirst combines two effectful actions, keeping only the result of the first. +func ApFirst[A, B any](second IO[B]) func(IO[A]) IO[A] { + return G.ApFirst[IO[A], IO[B], IO[func(B) A]](second) +} + +// MonadApSecond combines two effectful actions, keeping only the result of the second. +func MonadApSecond[A, B any](first IO[A], second IO[B]) IO[B] { + return G.MonadApSecond[IO[A], IO[B], IO[func(B) B]](first, second) +} + +// ApSecond combines two effectful actions, keeping only the result of the second. +func ApSecond[A, B any](second IO[B]) func(IO[A]) IO[B] { + return G.ApSecond[IO[A], IO[B], IO[func(B) B]](second) +} + +// MonadChainTo composes computations in sequence, ignoring the return value of the first computation +func MonadChainTo[A, B any](fa IO[A], fb IO[B]) IO[B] { + return G.MonadChainTo(fa, fb) +} + +// ChainTo composes computations in sequence, ignoring the return value of the first computation +func ChainTo[A, B any](fb IO[B]) func(IO[A]) IO[B] { + return G.ChainTo[IO[A]](fb) +} + +// Now returns the current timestamp +var Now = G.Now[IO[time.Time]]() + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[A any](gen func() IO[A]) IO[A] { + return G.Defer[IO[A]](gen) +} + +func MonadFlap[B, A any](fab IO[func(A) B], a A) IO[B] { + return G.MonadFlap[func(A) B, IO[func(A) B], IO[B], A, B](fab, a) +} + +func Flap[B, A any](a A) func(IO[func(A) B]) IO[B] { + return G.Flap[func(A) B, IO[func(A) B], IO[B], A, B](a) +} + +// Delay creates an operation that passes in the value after some delay +func Delay[A any](delay time.Duration) func(IO[A]) IO[A] { + return G.Delay[IO[A]](delay) +} + +// After creates an operation that passes after the given timestamp +func After[A any](timestamp time.Time) func(IO[A]) IO[A] { + return G.After[IO[A]](timestamp) +} + +// WithTime returns an operation that measures the start and end [time.Time] of the operation +func WithTime[A any](a IO[A]) IO[T.Tuple3[A, time.Time, time.Time]] { + return G.WithTime[IO[T.Tuple3[A, time.Time, time.Time]], IO[A]](a) +} + +// WithDuration returns an operation that measures the [time.Duration] +func WithDuration[A any](a IO[A]) IO[T.Tuple2[A, time.Duration]] { + return G.WithDuration[IO[T.Tuple2[A, time.Duration]], IO[A]](a) +} diff --git a/v2/io/io_test.go b/v2/io/io_test.go new file mode 100644 index 0000000..b0efcb8 --- /dev/null +++ b/v2/io/io_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "math/rand" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func TestMap(t *testing.T) { + assert.Equal(t, 2, F.Pipe1(Of(1), Map(utils.Double))()) +} + +func TestChain(t *testing.T) { + f := func(n int) IO[int] { + return Of(n * 2) + } + assert.Equal(t, 2, F.Pipe1(Of(1), Chain(f))()) +} + +func TestAp(t *testing.T) { + assert.Equal(t, 2, F.Pipe1(Of(utils.Double), Ap[int, int](Of(1)))()) +} + +func TestFlatten(t *testing.T) { + assert.Equal(t, 1, F.Pipe1(Of(Of(1)), Flatten[int])()) +} + +func TestMemoize(t *testing.T) { + data := Memoize(MakeIO(rand.Int)) + + value1 := data() + value2 := data() + + assert.Equal(t, value1, value2) +} + +func TestApFirst(t *testing.T) { + + x := F.Pipe1( + Of("a"), + ApFirst[string](Of("b")), + ) + + assert.Equal(t, "a", x()) +} + +func TestApSecond(t *testing.T) { + + x := F.Pipe1( + Of("a"), + ApSecond[string](Of("b")), + ) + + assert.Equal(t, "b", x()) +} diff --git a/v2/io/logging.go b/v2/io/logging.go new file mode 100644 index 0000000..f570f62 --- /dev/null +++ b/v2/io/logging.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "log" + + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Logger constructs a logger function that can be used with ChainXXXIOK +func Logger[A any](loggers ...*log.Logger) func(string) func(A) IO[any] { + return G.Logger[IO[any], A](loggers...) +} + +// Logf constructs a logger function that can be used with ChainXXXIOK +// the string prefix contains the format string for the log value +func Logf[A any](prefix string) func(A) IO[any] { + return G.Logf[IO[any], A](prefix) +} + +// Printf constructs a printer function that can be used with ChainXXXIOK +// the string prefix contains the format string for the log value +func Printf[A any](prefix string) func(A) IO[any] { + return G.Printf[IO[any], A](prefix) +} diff --git a/v2/io/logging_test.go b/v2/io/logging_test.go new file mode 100644 index 0000000..baa048f --- /dev/null +++ b/v2/io/logging_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLogger(t *testing.T) { + + l := Logger[int]() + + lio := l("out") + + assert.NotPanics(t, func() { lio(10)() }) +} + +func TestLogf(t *testing.T) { + + l := Logf[int] + + lio := l("Value is %d") + + assert.NotPanics(t, func() { lio(10)() }) +} diff --git a/v2/io/monad.go b/v2/io/monad.go new file mode 100644 index 0000000..df8cd05 --- /dev/null +++ b/v2/io/monad.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "github.com/IBM/fp-go/v2/internal/monad" + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Monad returns the monadic operations for [IO] +func Monad[A, B any]() monad.Monad[A, B, IO[A], IO[B], IO[func(A) B]] { + return G.Monad[A, B, IO[A], IO[B], IO[func(A) B]]() +} diff --git a/v2/io/pointed.go b/v2/io/pointed.go new file mode 100644 index 0000000..df78baa --- /dev/null +++ b/v2/io/pointed.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "github.com/IBM/fp-go/v2/internal/pointed" + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Pointed returns the monadic operations for [IO] +func Pointed[A any]() pointed.Pointed[A, IO[A]] { + return G.Pointed[A, IO[A]]() +} diff --git a/v2/io/resource.go b/v2/io/resource.go new file mode 100644 index 0000000..98ce80a --- /dev/null +++ b/v2/io/resource.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + G "github.com/IBM/fp-go/v2/io/generic" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[ + R, A, ANY any](onCreate IO[R], onRelease func(R) IO[ANY]) func(func(R) IO[A]) IO[A] { + // just dispatch + return G.WithResource[IO[A], IO[R], IO[ANY]](onCreate, onRelease) +} diff --git a/v2/io/retry.go b/v2/io/retry.go new file mode 100644 index 0000000..4e31cb5 --- /dev/null +++ b/v2/io/retry.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + G "github.com/IBM/fp-go/v2/io/generic" + R "github.com/IBM/fp-go/v2/retry" +) + +// Retrying will retry the actions according to the check policy +// +// policy - refers to the retry policy +// action - converts a status into an operation to be executed +// check - checks if the result of the action needs to be retried +func Retrying[A any]( + policy R.RetryPolicy, + action func(R.RetryStatus) IO[A], + check func(A) bool, +) IO[A] { + return G.Retrying(policy, action, check) +} diff --git a/v2/io/retry_test.go b/v2/io/retry_test.go new file mode 100644 index 0000000..780c996 --- /dev/null +++ b/v2/io/retry_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "fmt" + "strings" + "testing" + "time" + + R "github.com/IBM/fp-go/v2/retry" + "github.com/stretchr/testify/assert" +) + +var expLogBackoff = R.ExponentialBackoff(10) + +// our retry policy with a 1s cap +var testLogPolicy = R.CapDelay( + 2*time.Second, + R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)), +) + +func TestRetry(t *testing.T) { + action := func(status R.RetryStatus) IO[string] { + return Of(fmt.Sprintf("Retrying %d", status.IterNumber)) + } + check := func(value string) bool { + return !strings.Contains(value, "5") + } + + r := Retrying(testLogPolicy, action, check) + + assert.Equal(t, "Retrying 5", r()) +} diff --git a/v2/io/sequence_test.go b/v2/io/sequence_test.go new file mode 100644 index 0000000..d6aa5ba --- /dev/null +++ b/v2/io/sequence_test.go @@ -0,0 +1,65 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "fmt" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + TST "github.com/IBM/fp-go/v2/internal/testing" + "github.com/stretchr/testify/assert" + + "testing" +) + +func TestMapSeq(t *testing.T) { + var results []string + + handler := func(value string) IO[string] { + return func() string { + results = append(results, value) + return value + } + } + + src := A.From("a", "b", "c") + + res := F.Pipe2( + src, + TraverseArraySeq(handler), + Map(func(data []string) bool { + return assert.Equal(t, data, results) + }), + ) + + assert.True(t, res()) +} + +func TestSequenceArray(t *testing.T) { + + s := TST.SequenceArrayTest( + FromStrictEquals[bool](), + Pointed[string](), + Pointed[bool](), + Functor[[]string, bool](), + SequenceArray[string], + ) + + for i := 0; i < 10; i++ { + t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i)) + } +} diff --git a/v2/io/sync.go b/v2/io/sync.go new file mode 100644 index 0000000..1a22ca6 --- /dev/null +++ b/v2/io/sync.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + "context" + + G "github.com/IBM/fp-go/v2/io/generic" +) + +// WithLock executes the provided [IO] operation in the scope of a lock +func WithLock[A any](lock IO[context.CancelFunc]) func(fa IO[A]) IO[A] { + return G.WithLock[IO[A]](lock) +} diff --git a/v2/io/testing/laws.go b/v2/io/testing/laws.go new file mode 100644 index 0000000..fffd0fb --- /dev/null +++ b/v2/io/testing/laws.go @@ -0,0 +1,74 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" + "github.com/IBM/fp-go/v2/io" +) + +// AssertLaws asserts the apply monad laws for the `Either` monad +func AssertLaws[A, B, C any](t *testing.T, + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + return L.AssertLaws(t, + io.Eq(eqa), + io.Eq(eqb), + io.Eq(eqc), + + io.Of[A], + io.Of[B], + io.Of[C], + + io.Of[func(A) A], + io.Of[func(A) B], + io.Of[func(B) C], + io.Of[func(func(A) B) B], + + io.MonadMap[A, A], + io.MonadMap[A, B], + io.MonadMap[A, C], + io.MonadMap[B, C], + + io.MonadMap[func(B) C, func(func(A) B) func(A) C], + + io.MonadChain[A, A], + io.MonadChain[A, B], + io.MonadChain[A, C], + io.MonadChain[B, C], + + io.MonadAp[A, A], + io.MonadAp[B, A], + io.MonadAp[C, B], + io.MonadAp[C, A], + + io.MonadAp[B, func(A) B], + io.MonadAp[func(A) C, func(A) B], + + ab, + bc, + ) + +} diff --git a/v2/io/testing/laws_test.go b/v2/io/testing/laws_test.go new file mode 100644 index 0000000..43c0f37 --- /dev/null +++ b/v2/io/testing/laws_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqa, eqb, eqc, ab, bc) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/io/traverse.go b/v2/io/traverse.go new file mode 100644 index 0000000..2d790b2 --- /dev/null +++ b/v2/io/traverse.go @@ -0,0 +1,104 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io + +import ( + G "github.com/IBM/fp-go/v2/io/generic" +) + +func MonadTraverseArray[A, B any](tas []A, f func(A) IO[B]) IO[[]B] { + return G.MonadTraverseArray[IO[B], IO[[]B]](tas, f) +} + +// TraverseArray applies a function returning an [IO] to all elements in an array and the +// transforms this into an [IO] of that array +func TraverseArray[A, B any](f func(A) IO[B]) func([]A) IO[[]B] { + return G.TraverseArray[IO[B], IO[[]B], []A](f) +} + +// TraverseArrayWithIndex applies a function returning an [IO] to all elements in an array and the +// transforms this into an [IO] of that array +func TraverseArrayWithIndex[A, B any](f func(int, A) IO[B]) func([]A) IO[[]B] { + return G.TraverseArrayWithIndex[IO[B], IO[[]B], []A](f) +} + +// SequenceArray converts an array of [IO] to an [IO] of an array +func SequenceArray[A any](tas []IO[A]) IO[[]A] { + return G.SequenceArray[IO[A], IO[[]A]](tas) +} + +func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) IO[B]) IO[map[K]B] { + return G.MonadTraverseRecord[IO[map[K]B]](tas, f) +} + +// TraverseRecord applies a function returning an [IO] to all elements in a record and the +// transforms this into an [IO] of that record +func TraverseRecord[K comparable, A, B any](f func(A) IO[B]) func(map[K]A) IO[map[K]B] { + return G.TraverseRecord[IO[map[K]B], map[K]A, IO[B]](f) +} + +// TraverseRecordWithIndex applies a function returning an [IO] to all elements in a record and the +// transforms this into an [IO] of that record +func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) IO[B]) func(map[K]A) IO[map[K]B] { + return G.TraverseRecordWithIndex[IO[B], IO[map[K]B], map[K]A](f) +} + +// SequenceRecord converts a record of [IO] to an [IO] of a record +func SequenceRecord[K comparable, A any](tas map[K]IO[A]) IO[map[K]A] { + return G.SequenceRecord[IO[A], IO[map[K]A]](tas) +} + +func MonadTraverseArraySeq[A, B any](tas []A, f func(A) IO[B]) IO[[]B] { + return G.MonadTraverseArraySeq[IO[B], IO[[]B]](tas, f) +} + +// TraverseArraySeq applies a function returning an [IO] to all elements in an array and the +// transforms this into an [IO] of that array +func TraverseArraySeq[A, B any](f func(A) IO[B]) func([]A) IO[[]B] { + return G.TraverseArraySeq[IO[B], IO[[]B], []A](f) +} + +// TraverseArrayWithIndexSeq applies a function returning an [IO] to all elements in an array and the +// transforms this into an [IO] of that array +func TraverseArrayWithIndexSeq[A, B any](f func(int, A) IO[B]) func([]A) IO[[]B] { + return G.TraverseArrayWithIndexSeq[IO[B], IO[[]B], []A](f) +} + +// SequenceArraySeq converts an array of [IO] to an [IO] of an array +func SequenceArraySeq[A any](tas []IO[A]) IO[[]A] { + return G.SequenceArraySeq[IO[A], IO[[]A]](tas) +} + +func MonadTraverseRecordSeq[K comparable, A, B any](tas map[K]A, f func(A) IO[B]) IO[map[K]B] { + return G.MonadTraverseRecordSeq[IO[map[K]B]](tas, f) +} + +// TraverseRecord applies a function returning an [IO] to all elements in a record and the +// transforms this into an [IO] of that record +func TraverseRecordSeq[K comparable, A, B any](f func(A) IO[B]) func(map[K]A) IO[map[K]B] { + return G.TraverseRecordSeq[IO[map[K]B], map[K]A, IO[B]](f) +} + +// TraverseRecordWithIndexSeq applies a function returning an [IO] to all elements in a record and the +// transforms this into an [IO] of that record +func TraverseRecordWithIndeSeq[K comparable, A, B any](f func(K, A) IO[B]) func(map[K]A) IO[map[K]B] { + return G.TraverseRecordWithIndexSeq[IO[B], IO[map[K]B], map[K]A](f) +} + +// SequenceRecordSeq converts a record of [IO] to an [IO] of a record +func SequenceRecordSeq[K comparable, A any](tas map[K]IO[A]) IO[map[K]A] { + return G.SequenceRecordSeq[IO[A], IO[map[K]A]](tas) +} diff --git a/v2/ioeither/ap.go b/v2/ioeither/ap.go new file mode 100644 index 0000000..1165dee --- /dev/null +++ b/v2/ioeither/ap.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// MonadApFirst combines two effectful actions, keeping only the result of the first. +func MonadApFirst[A, E, B any](first IOEither[E, A], second IOEither[E, B]) IOEither[E, A] { + return G.MonadApFirst[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) A]](first, second) +} + +// ApFirst combines two effectful actions, keeping only the result of the first. +func ApFirst[A, E, B any](second IOEither[E, B]) func(IOEither[E, A]) IOEither[E, A] { + return G.ApFirst[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) A]](second) +} + +// MonadApSecond combines two effectful actions, keeping only the result of the second. +func MonadApSecond[A, E, B any](first IOEither[E, A], second IOEither[E, B]) IOEither[E, B] { + return G.MonadApSecond[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) B]](first, second) +} + +// ApSecond combines two effectful actions, keeping only the result of the second. +func ApSecond[A, E, B any](second IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] { + return G.ApSecond[IOEither[E, A], IOEither[E, B], IOEither[E, func(B) B]](second) +} diff --git a/v2/ioeither/bind.go b/v2/ioeither/bind.go new file mode 100644 index 0000000..7fa5196 --- /dev/null +++ b/v2/ioeither/bind.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[E, S any]( + empty S, +) IOEither[E, S] { + return G.Do[IOEither[E, S], E, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) IOEither[E, T], +) func(IOEither[E, S1]) IOEither[E, S2] { + return G.Bind[IOEither[E, S1], IOEither[E, S2], IOEither[E, T], E, S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(IOEither[E, S1]) IOEither[E, S2] { + return G.Let[IOEither[E, S1], IOEither[E, S2], E, S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[E, S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(IOEither[E, S1]) IOEither[E, S2] { + return G.LetTo[IOEither[E, S1], IOEither[E, S2], E, S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[E, S1, T any]( + setter func(T) S1, +) func(IOEither[E, T]) IOEither[E, S1] { + return G.BindTo[IOEither[E, S1], IOEither[E, T], E, S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa IOEither[E, T], +) func(IOEither[E, S1]) IOEither[E, S2] { + return G.ApS[IOEither[E, S1], IOEither[E, S2], IOEither[E, T], E, S1, S2, T](setter, fa) +} diff --git a/v2/ioeither/bind_test.go b/v2/ioeither/bind_test.go new file mode 100644 index 0000000..fe211a1 --- /dev/null +++ b/v2/ioeither/bind_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) IOEither[error, string] { + return Of[error]("Doe") +} + +func getGivenName(s utils.WithLastName) IOEither[error, string] { + return Of[error]("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do[error](utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map[error](utils.GetFullName), + ) + + assert.Equal(t, res(), E.Of[error]("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do[error](utils.Empty), + ApS(utils.SetLastName, Of[error]("Doe")), + ApS(utils.SetGivenName, Of[error]("John")), + Map[error](utils.GetFullName), + ) + + assert.Equal(t, res(), E.Of[error]("John Doe")) +} diff --git a/v2/ioeither/bracket.go b/v2/ioeither/bracket.go new file mode 100644 index 0000000..1d1a689 --- /dev/null +++ b/v2/ioeither/bracket.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[E, A, B, ANY any]( + acquire IOEither[E, A], + use func(A) IOEither[E, B], + release func(A, ET.Either[E, B]) IOEither[E, ANY], +) IOEither[E, B] { + return G.Bracket(acquire, use, release) +} diff --git a/v2/ioeither/doc.go b/v2/ioeither/doc.go new file mode 100644 index 0000000..5516056 --- /dev/null +++ b/v2/ioeither/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +//go:generate go run .. ioeither --count 10 --filename gen.go diff --git a/v2/ioeither/eq.go b/v2/ioeither/eq.go new file mode 100644 index 0000000..af0777d --- /dev/null +++ b/v2/ioeither/eq.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// Eq implements the equals predicate for values contained in the IOEither monad +func Eq[E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[IOEither[E, A]] { + return G.Eq[IOEither[E, A]](eq) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[E, A comparable]() EQ.Eq[IOEither[E, A]] { + return G.FromStrictEquals[IOEither[E, A]]() +} diff --git a/v2/ioeither/eq_test.go b/v2/ioeither/eq_test.go new file mode 100644 index 0000000..17ce26f --- /dev/null +++ b/v2/ioeither/eq_test.go @@ -0,0 +1,45 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEq(t *testing.T) { + + r1 := Of[string](1) + r2 := Of[string](1) + r3 := Of[string](2) + + e1 := Left[int]("a") + e2 := Left[int]("a") + e3 := Left[int]("b") + + eq := FromStrictEquals[string, int]() + + assert.True(t, eq.Equals(r1, r1)) + assert.True(t, eq.Equals(r1, r2)) + assert.False(t, eq.Equals(r1, r3)) + assert.False(t, eq.Equals(r1, e1)) + + assert.True(t, eq.Equals(e1, e1)) + assert.True(t, eq.Equals(e1, e2)) + assert.False(t, eq.Equals(e1, e3)) + assert.False(t, eq.Equals(e2, r2)) +} diff --git a/v2/ioeither/examples_create_test.go b/v2/ioeither/examples_create_test.go new file mode 100644 index 0000000..8c8ed0c --- /dev/null +++ b/v2/ioeither/examples_create_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "fmt" + + E "github.com/IBM/fp-go/v2/either" +) + +func ExampleIOEither_creation() { + // Build an IOEither + leftValue := Left[string](fmt.Errorf("some error")) + rightValue := Right[error]("value") + + // Convert from Either + eitherValue := E.Of[error](42) + ioFromEither := FromEither(eitherValue) + + // some predicate + isEven := func(num int) (int, error) { + if num%2 == 0 { + return num, nil + } + return 0, fmt.Errorf("%d is an odd number", num) + } + fromEven := Eitherize1(isEven) + leftFromPred := fromEven(3) + rightFromPred := fromEven(4) + + fmt.Println(leftValue()) + fmt.Println(rightValue()) + fmt.Println(ioFromEither()) + fmt.Println(leftFromPred()) + fmt.Println(rightFromPred()) + + // Output: + // Left[*errors.errorString](some error) + // Right[string](value) + // Right[int](42) + // Left[*errors.errorString](3 is an odd number) + // Right[int](4) + +} diff --git a/v2/ioeither/examples_do_test.go b/v2/ioeither/examples_do_test.go new file mode 100644 index 0000000..9ed636c --- /dev/null +++ b/v2/ioeither/examples_do_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "fmt" + "log" + + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" + T "github.com/IBM/fp-go/v2/tuple" +) + +func ExampleIOEither_do() { + foo := Of[error]("foo") + bar := Of[error](1) + + // quux consumes the state of three bindings and returns an [IO] instead of an [IOEither] + quux := func(t T.Tuple3[string, int, string]) IO.IO[any] { + return IO.FromImpure(func() { + log.Printf("t1: %s, t2: %d, t3: %s", t.F1, t.F2, t.F3) + }) + } + + transform := func(t T.Tuple3[string, int, string]) int { + return len(t.F1) + t.F2 + len(t.F3) + } + + b := F.Pipe5( + foo, + BindTo[error](T.Of[string]), + ApS(T.Push1[string, int], bar), + Bind(T.Push2[string, int, string], func(t T.Tuple2[string, int]) IOEither[error, string] { + return Of[error](fmt.Sprintf("%s%d", t.F1, t.F2)) + }), + ChainFirstIOK[error](quux), + Map[error](transform), + ) + + fmt.Println(b()) + + // Output: + // Right[int](8) +} diff --git a/v2/ioeither/examples_extract_test.go b/v2/ioeither/examples_extract_test.go new file mode 100644 index 0000000..7ad85bf --- /dev/null +++ b/v2/ioeither/examples_extract_test.go @@ -0,0 +1,45 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "fmt" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" +) + +func ExampleIOEither_extraction() { + // IOEither + someIOEither := Right[error](42) + eitherValue := someIOEither() // E.Right(42) + value := E.GetOrElse(F.Constant1[error](0))(eitherValue) // 42 + + // Or more directly + infaillibleIO := GetOrElse(F.Constant1[error](IO.Of(0)))(someIOEither) // => IO.Right(42) + valueFromIO := infaillibleIO() // => 42 + + fmt.Println(eitherValue) + fmt.Println(value) + fmt.Println(valueFromIO) + + // Output: + // Right[int](42) + // 42 + // 42 + +} diff --git a/v2/ioeither/exec/exec.go b/v2/ioeither/exec/exec.go new file mode 100644 index 0000000..3986f94 --- /dev/null +++ b/v2/ioeither/exec/exec.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package exec + +import ( + "github.com/IBM/fp-go/v2/exec" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +var ( + // Command executes a command + Command = F.Curry3(G.Command[IOE.IOEither[error, exec.CommandOutput]]) +) diff --git a/v2/ioeither/exec/exec_test.go b/v2/ioeither/exec/exec_test.go new file mode 100644 index 0000000..a04557d --- /dev/null +++ b/v2/ioeither/exec/exec_test.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package exec + +import ( + "strings" + "testing" + + RA "github.com/IBM/fp-go/v2/array" + B "github.com/IBM/fp-go/v2/bytes" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/exec" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + "github.com/stretchr/testify/assert" +) + +func TestOpenSSL(t *testing.T) { + // execute the openSSL binary + version := F.Pipe1( + Command("openssl")(RA.From("version"))(B.Monoid.Empty()), + IOE.Map[error](F.Flow3( + exec.StdOut, + B.ToString, + strings.TrimSpace, + )), + ) + + assert.True(t, E.IsRight(version())) +} diff --git a/v2/ioeither/file/dir.go b/v2/ioeither/file/dir.go new file mode 100644 index 0000000..8fc4db8 --- /dev/null +++ b/v2/ioeither/file/dir.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "os" + + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +// MkdirAll create a sequence of directories, see [os.MkdirAll] +func MkdirAll(path string, perm os.FileMode) IOE.IOEither[error, string] { + return IOE.TryCatchError(func() (string, error) { + return path, os.MkdirAll(path, perm) + }) +} + +// Mkdir create a directory, see [os.Mkdir] +func Mkdir(path string, perm os.FileMode) IOE.IOEither[error, string] { + return IOE.TryCatchError(func() (string, error) { + return path, os.Mkdir(path, perm) + }) +} diff --git a/v2/ioeither/file/file.go b/v2/ioeither/file/file.go new file mode 100644 index 0000000..17100a5 --- /dev/null +++ b/v2/ioeither/file/file.go @@ -0,0 +1,55 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "io" + "os" + + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +var ( + // Open opens a file for reading + Open = IOE.Eitherize1(os.Open) + // Create opens a file for writing + Create = IOE.Eitherize1(os.Create) + // ReadFile reads the context of a file + ReadFile = IOE.Eitherize1(os.ReadFile) +) + +// WriteFile writes a data blob to a file +func WriteFile(dstName string, perm os.FileMode) func([]byte) IOE.IOEither[error, []byte] { + return func(data []byte) IOE.IOEither[error, []byte] { + return IOE.TryCatchError(func() ([]byte, error) { + return data, os.WriteFile(dstName, data, perm) + }) + } +} + +// Remove removes a file by name +func Remove(name string) IOE.IOEither[error, string] { + return IOE.TryCatchError(func() (string, error) { + return name, os.Remove(name) + }) +} + +// Close closes an object +func Close[C io.Closer](c C) IOE.IOEither[error, any] { + return IOE.TryCatchError(func() (any, error) { + return c, c.Close() + }) +} diff --git a/v2/ioeither/file/readall.go b/v2/ioeither/file/readall.go new file mode 100644 index 0000000..6e52c13 --- /dev/null +++ b/v2/ioeither/file/readall.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "io" + + FL "github.com/IBM/fp-go/v2/file" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +var ( + // readAll is the adapted version of [io.ReadAll] + readAll = IOE.Eitherize1(io.ReadAll) +) + +// ReadAll uses a generator function to create a stream, reads it and closes it +func ReadAll[R io.ReadCloser](acquire IOE.IOEither[error, R]) IOE.IOEither[error, []byte] { + return F.Pipe1( + F.Flow2( + FL.ToReader[R], + readAll, + ), + IOE.WithResource[[]byte](acquire, Close[R]), + ) +} diff --git a/v2/ioeither/file/tempfile.go b/v2/ioeither/file/tempfile.go new file mode 100644 index 0000000..175feca --- /dev/null +++ b/v2/ioeither/file/tempfile.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "os" + + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" + IOF "github.com/IBM/fp-go/v2/io/file" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +var ( + // CreateTemp created a temp file with proper parametrization + CreateTemp = IOE.Eitherize2(os.CreateTemp) + // onCreateTempFile creates a temp file with sensible defaults + onCreateTempFile = CreateTemp("", "*") + // destroy handler + onReleaseTempFile = F.Flow4( + IOF.Close[*os.File], + IO.Map((*os.File).Name), + IOE.FromIO[error, string], + IOE.Chain(Remove), + ) +) + +// WithTempFile creates a temporary file, then invokes a callback to create a resource based on the file, then close and remove the temp file +func WithTempFile[A any](f func(*os.File) IOE.IOEither[error, A]) IOE.IOEither[error, A] { + return IOE.WithResource[A](onCreateTempFile, onReleaseTempFile)(f) +} diff --git a/v2/ioeither/file/tempfile_test.go b/v2/ioeither/file/tempfile_test.go new file mode 100644 index 0000000..950c1d0 --- /dev/null +++ b/v2/ioeither/file/tempfile_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "os" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + "github.com/stretchr/testify/assert" +) + +func TestWithTempFile(t *testing.T) { + + res := WithTempFile(onWriteAll[*os.File]([]byte("Carsten"))) + + assert.Equal(t, E.Of[error]([]byte("Carsten")), res()) +} + +func TestWithTempFileOnClosedFile(t *testing.T) { + + res := WithTempFile(func(f *os.File) IOE.IOEither[error, []byte] { + return F.Pipe2( + f, + onWriteAll[*os.File]([]byte("Carsten")), + IOE.ChainFirst(F.Constant1[[]byte](Close(f))), + ) + }) + + assert.Equal(t, E.Of[error]([]byte("Carsten")), res()) +} diff --git a/v2/ioeither/file/write.go b/v2/ioeither/file/write.go new file mode 100644 index 0000000..2a2ec62 --- /dev/null +++ b/v2/ioeither/file/write.go @@ -0,0 +1,50 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package file + +import ( + "io" + + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +func onWriteAll[W io.Writer](data []byte) func(w W) IOE.IOEither[error, []byte] { + return func(w W) IOE.IOEither[error, []byte] { + return IOE.TryCatchError(func() ([]byte, error) { + _, err := w.Write(data) + return data, err + }) + } +} + +// WriteAll uses a generator function to create a stream, writes data to it and closes it +func WriteAll[W io.WriteCloser](data []byte) func(acquire IOE.IOEither[error, W]) IOE.IOEither[error, []byte] { + onWrite := onWriteAll[W](data) + return func(onCreate IOE.IOEither[error, W]) IOE.IOEither[error, []byte] { + return IOE.WithResource[[]byte]( + onCreate, + Close[W])( + onWrite, + ) + } +} + +// Write uses a generator function to create a stream, writes data to it and closes it +func Write[R any, W io.WriteCloser](acquire IOE.IOEither[error, W]) func(use func(W) IOE.IOEither[error, R]) IOE.IOEither[error, R] { + return IOE.WithResource[R]( + acquire, + Close[W]) +} diff --git a/v2/ioeither/gen.go b/v2/ioeither/gen.go new file mode 100644 index 0000000..b8b8222 --- /dev/null +++ b/v2/ioeither/gen.go @@ -0,0 +1,485 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:13.144922 +0100 CET m=+0.065757301 + +package ioeither + +import ( + G "github.com/IBM/fp-go/v2/ioeither/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// Eitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [IOEither[error, R]] +func Eitherize0[F ~func() (R, error), R any](f F) func() IOEither[error, R] { + return G.Eitherize0[IOEither[error, R]](f) +} + +// Uneitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [IOEither[error, R]] +func Uneitherize0[F ~func() IOEither[error, R], R any](f F) func() (R, error) { + return G.Uneitherize0[IOEither[error, R]](f) +} + +// Eitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [IOEither[error, R]] +func Eitherize1[F ~func(T1) (R, error), T1, R any](f F) func(T1) IOEither[error, R] { + return G.Eitherize1[IOEither[error, R]](f) +} + +// Uneitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [IOEither[error, R]] +func Uneitherize1[F ~func(T1) IOEither[error, R], T1, R any](f F) func(T1) (R, error) { + return G.Uneitherize1[IOEither[error, R]](f) +} + +// SequenceT1 converts 1 [IOEither[E, T]] into a [IOEither[E, T.Tuple1[T1]]] +func SequenceT1[E, T1 any]( + t1 IOEither[E, T1], +) IOEither[E, T.Tuple1[T1]] { + return G.SequenceT1[ + IOEither[E, T.Tuple1[T1]], + IOEither[E, T1], + ](t1) +} + +// SequenceTuple1 converts a [T.Tuple1[IOEither[E, T]]] into a [IOEither[E, T.Tuple1[T1]]] +func SequenceTuple1[E, T1 any](t T.Tuple1[IOEither[E, T1]]) IOEither[E, T.Tuple1[T1]] { + return G.SequenceTuple1[ + IOEither[E, T.Tuple1[T1]], + IOEither[E, T1], + ](t) +} + +// TraverseTuple1 converts a [T.Tuple1[IOEither[E, T]]] into a [IOEither[E, T.Tuple1[T1]]] +func TraverseTuple1[F1 ~func(A1) IOEither[E, T1], E, A1, T1 any](f1 F1) func(T.Tuple1[A1]) IOEither[E, T.Tuple1[T1]] { + return G.TraverseTuple1[IOEither[E, T.Tuple1[T1]]](f1) +} + +// Eitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [IOEither[error, R]] +func Eitherize2[F ~func(T1, T2) (R, error), T1, T2, R any](f F) func(T1, T2) IOEither[error, R] { + return G.Eitherize2[IOEither[error, R]](f) +} + +// Uneitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [IOEither[error, R]] +func Uneitherize2[F ~func(T1, T2) IOEither[error, R], T1, T2, R any](f F) func(T1, T2) (R, error) { + return G.Uneitherize2[IOEither[error, R]](f) +} + +// SequenceT2 converts 2 [IOEither[E, T]] into a [IOEither[E, T.Tuple2[T1, T2]]] +func SequenceT2[E, T1, T2 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], +) IOEither[E, T.Tuple2[T1, T2]] { + return G.SequenceT2[ + IOEither[E, T.Tuple2[T1, T2]], + IOEither[E, T1], + IOEither[E, T2], + ](t1, t2) +} + +// SequenceTuple2 converts a [T.Tuple2[IOEither[E, T]]] into a [IOEither[E, T.Tuple2[T1, T2]]] +func SequenceTuple2[E, T1, T2 any](t T.Tuple2[IOEither[E, T1], IOEither[E, T2]]) IOEither[E, T.Tuple2[T1, T2]] { + return G.SequenceTuple2[ + IOEither[E, T.Tuple2[T1, T2]], + IOEither[E, T1], + IOEither[E, T2], + ](t) +} + +// TraverseTuple2 converts a [T.Tuple2[IOEither[E, T]]] into a [IOEither[E, T.Tuple2[T1, T2]]] +func TraverseTuple2[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], E, A1, A2, T1, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) IOEither[E, T.Tuple2[T1, T2]] { + return G.TraverseTuple2[IOEither[E, T.Tuple2[T1, T2]]](f1, f2) +} + +// Eitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [IOEither[error, R]] +func Eitherize3[F ~func(T1, T2, T3) (R, error), T1, T2, T3, R any](f F) func(T1, T2, T3) IOEither[error, R] { + return G.Eitherize3[IOEither[error, R]](f) +} + +// Uneitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [IOEither[error, R]] +func Uneitherize3[F ~func(T1, T2, T3) IOEither[error, R], T1, T2, T3, R any](f F) func(T1, T2, T3) (R, error) { + return G.Uneitherize3[IOEither[error, R]](f) +} + +// SequenceT3 converts 3 [IOEither[E, T]] into a [IOEither[E, T.Tuple3[T1, T2, T3]]] +func SequenceT3[E, T1, T2, T3 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], + t3 IOEither[E, T3], +) IOEither[E, T.Tuple3[T1, T2, T3]] { + return G.SequenceT3[ + IOEither[E, T.Tuple3[T1, T2, T3]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + ](t1, t2, t3) +} + +// SequenceTuple3 converts a [T.Tuple3[IOEither[E, T]]] into a [IOEither[E, T.Tuple3[T1, T2, T3]]] +func SequenceTuple3[E, T1, T2, T3 any](t T.Tuple3[IOEither[E, T1], IOEither[E, T2], IOEither[E, T3]]) IOEither[E, T.Tuple3[T1, T2, T3]] { + return G.SequenceTuple3[ + IOEither[E, T.Tuple3[T1, T2, T3]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + ](t) +} + +// TraverseTuple3 converts a [T.Tuple3[IOEither[E, T]]] into a [IOEither[E, T.Tuple3[T1, T2, T3]]] +func TraverseTuple3[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], F3 ~func(A3) IOEither[E, T3], E, A1, A2, A3, T1, T2, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) IOEither[E, T.Tuple3[T1, T2, T3]] { + return G.TraverseTuple3[IOEither[E, T.Tuple3[T1, T2, T3]]](f1, f2, f3) +} + +// Eitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [IOEither[error, R]] +func Eitherize4[F ~func(T1, T2, T3, T4) (R, error), T1, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) IOEither[error, R] { + return G.Eitherize4[IOEither[error, R]](f) +} + +// Uneitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [IOEither[error, R]] +func Uneitherize4[F ~func(T1, T2, T3, T4) IOEither[error, R], T1, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) (R, error) { + return G.Uneitherize4[IOEither[error, R]](f) +} + +// SequenceT4 converts 4 [IOEither[E, T]] into a [IOEither[E, T.Tuple4[T1, T2, T3, T4]]] +func SequenceT4[E, T1, T2, T3, T4 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], + t3 IOEither[E, T3], + t4 IOEither[E, T4], +) IOEither[E, T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceT4[ + IOEither[E, T.Tuple4[T1, T2, T3, T4]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + ](t1, t2, t3, t4) +} + +// SequenceTuple4 converts a [T.Tuple4[IOEither[E, T]]] into a [IOEither[E, T.Tuple4[T1, T2, T3, T4]]] +func SequenceTuple4[E, T1, T2, T3, T4 any](t T.Tuple4[IOEither[E, T1], IOEither[E, T2], IOEither[E, T3], IOEither[E, T4]]) IOEither[E, T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceTuple4[ + IOEither[E, T.Tuple4[T1, T2, T3, T4]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + ](t) +} + +// TraverseTuple4 converts a [T.Tuple4[IOEither[E, T]]] into a [IOEither[E, T.Tuple4[T1, T2, T3, T4]]] +func TraverseTuple4[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], F3 ~func(A3) IOEither[E, T3], F4 ~func(A4) IOEither[E, T4], E, A1, A2, A3, A4, T1, T2, T3, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) IOEither[E, T.Tuple4[T1, T2, T3, T4]] { + return G.TraverseTuple4[IOEither[E, T.Tuple4[T1, T2, T3, T4]]](f1, f2, f3, f4) +} + +// Eitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [IOEither[error, R]] +func Eitherize5[F ~func(T1, T2, T3, T4, T5) (R, error), T1, T2, T3, T4, T5, R any](f F) func(T1, T2, T3, T4, T5) IOEither[error, R] { + return G.Eitherize5[IOEither[error, R]](f) +} + +// Uneitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [IOEither[error, R]] +func Uneitherize5[F ~func(T1, T2, T3, T4, T5) IOEither[error, R], T1, T2, T3, T4, T5, R any](f F) func(T1, T2, T3, T4, T5) (R, error) { + return G.Uneitherize5[IOEither[error, R]](f) +} + +// SequenceT5 converts 5 [IOEither[E, T]] into a [IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceT5[E, T1, T2, T3, T4, T5 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], + t3 IOEither[E, T3], + t4 IOEither[E, T4], + t5 IOEither[E, T5], +) IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceT5[ + IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + ](t1, t2, t3, t4, t5) +} + +// SequenceTuple5 converts a [T.Tuple5[IOEither[E, T]]] into a [IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceTuple5[E, T1, T2, T3, T4, T5 any](t T.Tuple5[IOEither[E, T1], IOEither[E, T2], IOEither[E, T3], IOEither[E, T4], IOEither[E, T5]]) IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceTuple5[ + IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + ](t) +} + +// TraverseTuple5 converts a [T.Tuple5[IOEither[E, T]]] into a [IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]]] +func TraverseTuple5[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], F3 ~func(A3) IOEither[E, T3], F4 ~func(A4) IOEither[E, T4], F5 ~func(A5) IOEither[E, T5], E, A1, A2, A3, A4, A5, T1, T2, T3, T4, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]] { + return G.TraverseTuple5[IOEither[E, T.Tuple5[T1, T2, T3, T4, T5]]](f1, f2, f3, f4, f5) +} + +// Eitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [IOEither[error, R]] +func Eitherize6[F ~func(T1, T2, T3, T4, T5, T6) (R, error), T1, T2, T3, T4, T5, T6, R any](f F) func(T1, T2, T3, T4, T5, T6) IOEither[error, R] { + return G.Eitherize6[IOEither[error, R]](f) +} + +// Uneitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [IOEither[error, R]] +func Uneitherize6[F ~func(T1, T2, T3, T4, T5, T6) IOEither[error, R], T1, T2, T3, T4, T5, T6, R any](f F) func(T1, T2, T3, T4, T5, T6) (R, error) { + return G.Uneitherize6[IOEither[error, R]](f) +} + +// SequenceT6 converts 6 [IOEither[E, T]] into a [IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceT6[E, T1, T2, T3, T4, T5, T6 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], + t3 IOEither[E, T3], + t4 IOEither[E, T4], + t5 IOEither[E, T5], + t6 IOEither[E, T6], +) IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceT6[ + IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + ](t1, t2, t3, t4, t5, t6) +} + +// SequenceTuple6 converts a [T.Tuple6[IOEither[E, T]]] into a [IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceTuple6[E, T1, T2, T3, T4, T5, T6 any](t T.Tuple6[IOEither[E, T1], IOEither[E, T2], IOEither[E, T3], IOEither[E, T4], IOEither[E, T5], IOEither[E, T6]]) IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceTuple6[ + IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + ](t) +} + +// TraverseTuple6 converts a [T.Tuple6[IOEither[E, T]]] into a [IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func TraverseTuple6[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], F3 ~func(A3) IOEither[E, T3], F4 ~func(A4) IOEither[E, T4], F5 ~func(A5) IOEither[E, T5], F6 ~func(A6) IOEither[E, T6], E, A1, A2, A3, A4, A5, A6, T1, T2, T3, T4, T5, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.TraverseTuple6[IOEither[E, T.Tuple6[T1, T2, T3, T4, T5, T6]]](f1, f2, f3, f4, f5, f6) +} + +// Eitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [IOEither[error, R]] +func Eitherize7[F ~func(T1, T2, T3, T4, T5, T6, T7) (R, error), T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T1, T2, T3, T4, T5, T6, T7) IOEither[error, R] { + return G.Eitherize7[IOEither[error, R]](f) +} + +// Uneitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [IOEither[error, R]] +func Uneitherize7[F ~func(T1, T2, T3, T4, T5, T6, T7) IOEither[error, R], T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T1, T2, T3, T4, T5, T6, T7) (R, error) { + return G.Uneitherize7[IOEither[error, R]](f) +} + +// SequenceT7 converts 7 [IOEither[E, T]] into a [IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceT7[E, T1, T2, T3, T4, T5, T6, T7 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], + t3 IOEither[E, T3], + t4 IOEither[E, T4], + t5 IOEither[E, T5], + t6 IOEither[E, T6], + t7 IOEither[E, T7], +) IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceT7[ + IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + IOEither[E, T7], + ](t1, t2, t3, t4, t5, t6, t7) +} + +// SequenceTuple7 converts a [T.Tuple7[IOEither[E, T]]] into a [IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceTuple7[E, T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[IOEither[E, T1], IOEither[E, T2], IOEither[E, T3], IOEither[E, T4], IOEither[E, T5], IOEither[E, T6], IOEither[E, T7]]) IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceTuple7[ + IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + IOEither[E, T7], + ](t) +} + +// TraverseTuple7 converts a [T.Tuple7[IOEither[E, T]]] into a [IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func TraverseTuple7[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], F3 ~func(A3) IOEither[E, T3], F4 ~func(A4) IOEither[E, T4], F5 ~func(A5) IOEither[E, T5], F6 ~func(A6) IOEither[E, T6], F7 ~func(A7) IOEither[E, T7], E, A1, A2, A3, A4, A5, A6, A7, T1, T2, T3, T4, T5, T6, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.TraverseTuple7[IOEither[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](f1, f2, f3, f4, f5, f6, f7) +} + +// Eitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [IOEither[error, R]] +func Eitherize8[F ~func(T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8) IOEither[error, R] { + return G.Eitherize8[IOEither[error, R]](f) +} + +// Uneitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [IOEither[error, R]] +func Uneitherize8[F ~func(T1, T2, T3, T4, T5, T6, T7, T8) IOEither[error, R], T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8) (R, error) { + return G.Uneitherize8[IOEither[error, R]](f) +} + +// SequenceT8 converts 8 [IOEither[E, T]] into a [IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceT8[E, T1, T2, T3, T4, T5, T6, T7, T8 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], + t3 IOEither[E, T3], + t4 IOEither[E, T4], + t5 IOEither[E, T5], + t6 IOEither[E, T6], + t7 IOEither[E, T7], + t8 IOEither[E, T8], +) IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceT8[ + IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + IOEither[E, T7], + IOEither[E, T8], + ](t1, t2, t3, t4, t5, t6, t7, t8) +} + +// SequenceTuple8 converts a [T.Tuple8[IOEither[E, T]]] into a [IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceTuple8[E, T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[IOEither[E, T1], IOEither[E, T2], IOEither[E, T3], IOEither[E, T4], IOEither[E, T5], IOEither[E, T6], IOEither[E, T7], IOEither[E, T8]]) IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceTuple8[ + IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + IOEither[E, T7], + IOEither[E, T8], + ](t) +} + +// TraverseTuple8 converts a [T.Tuple8[IOEither[E, T]]] into a [IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func TraverseTuple8[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], F3 ~func(A3) IOEither[E, T3], F4 ~func(A4) IOEither[E, T4], F5 ~func(A5) IOEither[E, T5], F6 ~func(A6) IOEither[E, T6], F7 ~func(A7) IOEither[E, T7], F8 ~func(A8) IOEither[E, T8], E, A1, A2, A3, A4, A5, A6, A7, A8, T1, T2, T3, T4, T5, T6, T7, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.TraverseTuple8[IOEither[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](f1, f2, f3, f4, f5, f6, f7, f8) +} + +// Eitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [IOEither[error, R]] +func Eitherize9[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9) IOEither[error, R] { + return G.Eitherize9[IOEither[error, R]](f) +} + +// Uneitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [IOEither[error, R]] +func Uneitherize9[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9) IOEither[error, R], T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) { + return G.Uneitherize9[IOEither[error, R]](f) +} + +// SequenceT9 converts 9 [IOEither[E, T]] into a [IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceT9[E, T1, T2, T3, T4, T5, T6, T7, T8, T9 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], + t3 IOEither[E, T3], + t4 IOEither[E, T4], + t5 IOEither[E, T5], + t6 IOEither[E, T6], + t7 IOEither[E, T7], + t8 IOEither[E, T8], + t9 IOEither[E, T9], +) IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceT9[ + IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + IOEither[E, T7], + IOEither[E, T8], + IOEither[E, T9], + ](t1, t2, t3, t4, t5, t6, t7, t8, t9) +} + +// SequenceTuple9 converts a [T.Tuple9[IOEither[E, T]]] into a [IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceTuple9[E, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[IOEither[E, T1], IOEither[E, T2], IOEither[E, T3], IOEither[E, T4], IOEither[E, T5], IOEither[E, T6], IOEither[E, T7], IOEither[E, T8], IOEither[E, T9]]) IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceTuple9[ + IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + IOEither[E, T7], + IOEither[E, T8], + IOEither[E, T9], + ](t) +} + +// TraverseTuple9 converts a [T.Tuple9[IOEither[E, T]]] into a [IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func TraverseTuple9[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], F3 ~func(A3) IOEither[E, T3], F4 ~func(A4) IOEither[E, T4], F5 ~func(A5) IOEither[E, T5], F6 ~func(A6) IOEither[E, T6], F7 ~func(A7) IOEither[E, T7], F8 ~func(A8) IOEither[E, T8], F9 ~func(A9) IOEither[E, T9], E, A1, A2, A3, A4, A5, A6, A7, A8, A9, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.TraverseTuple9[IOEither[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](f1, f2, f3, f4, f5, f6, f7, f8, f9) +} + +// Eitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [IOEither[error, R]] +func Eitherize10[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) IOEither[error, R] { + return G.Eitherize10[IOEither[error, R]](f) +} + +// Uneitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [IOEither[error, R]] +func Uneitherize10[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) IOEither[error, R], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) (R, error) { + return G.Uneitherize10[IOEither[error, R]](f) +} + +// SequenceT10 converts 10 [IOEither[E, T]] into a [IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceT10[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any]( + t1 IOEither[E, T1], + t2 IOEither[E, T2], + t3 IOEither[E, T3], + t4 IOEither[E, T4], + t5 IOEither[E, T5], + t6 IOEither[E, T6], + t7 IOEither[E, T7], + t8 IOEither[E, T8], + t9 IOEither[E, T9], + t10 IOEither[E, T10], +) IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceT10[ + IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + IOEither[E, T7], + IOEither[E, T8], + IOEither[E, T9], + IOEither[E, T10], + ](t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) +} + +// SequenceTuple10 converts a [T.Tuple10[IOEither[E, T]]] into a [IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceTuple10[E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[IOEither[E, T1], IOEither[E, T2], IOEither[E, T3], IOEither[E, T4], IOEither[E, T5], IOEither[E, T6], IOEither[E, T7], IOEither[E, T8], IOEither[E, T9], IOEither[E, T10]]) IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceTuple10[ + IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + IOEither[E, T1], + IOEither[E, T2], + IOEither[E, T3], + IOEither[E, T4], + IOEither[E, T5], + IOEither[E, T6], + IOEither[E, T7], + IOEither[E, T8], + IOEither[E, T9], + IOEither[E, T10], + ](t) +} + +// TraverseTuple10 converts a [T.Tuple10[IOEither[E, T]]] into a [IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func TraverseTuple10[F1 ~func(A1) IOEither[E, T1], F2 ~func(A2) IOEither[E, T2], F3 ~func(A3) IOEither[E, T3], F4 ~func(A4) IOEither[E, T4], F5 ~func(A5) IOEither[E, T5], F6 ~func(A6) IOEither[E, T6], F7 ~func(A7) IOEither[E, T7], F8 ~func(A8) IOEither[E, T8], F9 ~func(A9) IOEither[E, T9], F10 ~func(A10) IOEither[E, T10], E, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.TraverseTuple10[IOEither[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) +} diff --git a/v2/ioeither/generic/ap.go b/v2/ioeither/generic/ap.go new file mode 100644 index 0000000..288b3da --- /dev/null +++ b/v2/ioeither/generic/ap.go @@ -0,0 +1,63 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/internal/apply" +) + +// MonadApFirst combines two effectful actions, keeping only the result of the first. +func MonadApFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GBA ~func() ET.Either[E, func(B) A], E, A, B any](first GA, second GB) GA { + return G.MonadApFirst( + MonadAp[GA, GBA, GB], + MonadMap[GA, GBA, E, A, func(B) A], + + first, + second, + ) +} + +// ApFirst combines two effectful actions, keeping only the result of the first. +func ApFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GBA ~func() ET.Either[E, func(B) A], E, A, B any](second GB) func(GA) GA { + return G.ApFirst( + MonadAp[GA, GBA, GB], + MonadMap[GA, GBA, E, A, func(B) A], + + second, + ) +} + +// MonadApSecond combines two effectful actions, keeping only the result of the second. +func MonadApSecond[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GBB ~func() ET.Either[E, func(B) B], E, A, B any](first GA, second GB) GB { + return G.MonadApSecond( + MonadAp[GB, GBB, GB], + MonadMap[GA, GBB, E, A, func(B) B], + + first, + second, + ) +} + +// ApSecond combines two effectful actions, keeping only the result of the second. +func ApSecond[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GBB ~func() ET.Either[E, func(B) B], E, A, B any](second GB) func(GA) GB { + return G.ApSecond( + MonadAp[GB, GBB, GB], + MonadMap[GA, GBB, E, A, func(B) B], + + second, + ) +} diff --git a/v2/ioeither/generic/bind.go b/v2/ioeither/generic/bind.go new file mode 100644 index 0000000..a43b99f --- /dev/null +++ b/v2/ioeither/generic/bind.go @@ -0,0 +1,90 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GS ~func() ET.Either[E, S], E, S any]( + empty S, +) GS { + return Of[GS, E, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GT, +) func(GS1) GS2 { + return C.Bind( + Chain[GS1, GS2, E, S1, S2], + Map[GT, GS2, E, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], E, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GS1) GS2 { + return F.Let( + Map[GS1, GS2, E, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], E, S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GS1) GS2 { + return F.LetTo( + Map[GS1, GS2, E, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GS1 ~func() ET.Either[E, S1], GT ~func() ET.Either[E, T], E, S1, S2, T any]( + setter func(T) S1, +) func(GT) GS1 { + return C.BindTo( + Map[GT, GS1, E, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa GT, +) func(GS1) GS2 { + return A.ApS( + Ap[GS2, func() ET.Either[E, func(T) S2], GT, E, T, S2], + Map[GS1, func() ET.Either[E, func(T) S2], E, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/ioeither/generic/bracket.go b/v2/ioeither/generic/bracket.go new file mode 100644 index 0000000..97c09bd --- /dev/null +++ b/v2/ioeither/generic/bracket.go @@ -0,0 +1,46 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/internal/bracket" + I "github.com/IBM/fp-go/v2/io/generic" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[ + GA ~func() ET.Either[E, A], + GB ~func() ET.Either[E, B], + GANY ~func() ET.Either[E, ANY], + E, A, B, ANY any]( + + acquire GA, + use func(A) GB, + release func(A, ET.Either[E, B]) GANY, +) GB { + return G.Bracket[GA, GB, GANY, ET.Either[E, B], A, B]( + I.Of[GB, ET.Either[E, B]], + MonadChain[GA, GB, E, A, B], + I.MonadChain[GB, GB, ET.Either[E, B], ET.Either[E, B]], + MonadChain[GANY, GB, E, ANY, B], + + acquire, + use, + release, + ) +} diff --git a/v2/ioeither/generic/eq.go b/v2/ioeither/generic/eq.go new file mode 100644 index 0000000..8a92b9f --- /dev/null +++ b/v2/ioeither/generic/eq.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Eq implements the equals predicate for values contained in the IOEither monad +func Eq[GA ~func() ET.Either[E, A], E, A any](eq EQ.Eq[ET.Either[E, A]]) EQ.Eq[GA] { + return G.Eq[GA](eq) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[GA ~func() ET.Either[E, A], E, A comparable]() EQ.Eq[GA] { + return Eq[GA](ET.FromStrictEquals[E, A]()) +} diff --git a/v2/ioeither/generic/exec.go b/v2/ioeither/generic/exec.go new file mode 100644 index 0000000..be47913 --- /dev/null +++ b/v2/ioeither/generic/exec.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + ET "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/exec" + GE "github.com/IBM/fp-go/v2/internal/exec" +) + +// Command executes a command +func Command[GA ~func() ET.Either[error, exec.CommandOutput]](name string, args []string, in []byte) GA { + return TryCatchError[GA](func() (exec.CommandOutput, error) { + return GE.Exec(context.Background(), name, args, in) + }) +} diff --git a/v2/ioeither/generic/gen.go b/v2/ioeither/generic/gen.go new file mode 100644 index 0000000..57e3898 --- /dev/null +++ b/v2/ioeither/generic/gen.go @@ -0,0 +1,1017 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:13.1643755 +0100 CET m=+0.085210801 +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// Eitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning a [GIOA] +func Eitherize0[GIOA ~func() ET.Either[error, R], F ~func() (R, error), R any](f F) func() GIOA { + e := ET.Eitherize0(f) + return func() GIOA { + return func() ET.Either[error, R] { + return e() + } + } +} + +// Uneitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning a [GIOA] +func Uneitherize0[GIOA ~func() ET.Either[error, R], GTA ~func() GIOA, R any](f GTA) func() (R, error) { + return func() (R, error) { + return ET.Unwrap(f()()) + } +} + +// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [GIOA] +func Eitherize1[GIOA ~func() ET.Either[error, R], F ~func(T1) (R, error), T1, R any](f F) func(T1) GIOA { + e := ET.Eitherize1(f) + return func(t1 T1) GIOA { + return func() ET.Either[error, R] { + return e(t1) + } + } +} + +// Uneitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [GIOA] +func Uneitherize1[GIOA ~func() ET.Either[error, R], GTA ~func(T1) GIOA, T1, R any](f GTA) func(T1) (R, error) { + return func(t1 T1) (R, error) { + return ET.Unwrap(f(t1)()) + } +} + +// SequenceT1 converts 1 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple1[T1]]] +func SequenceT1[ + G_TUPLE1 ~func() ET.Either[E, T.Tuple1[T1]], + G_T1 ~func() ET.Either[E, T1], + E, T1 any]( + t1 G_T1, +) G_TUPLE1 { + return A.SequenceT1( + Map[G_T1, G_TUPLE1, E, T1, T.Tuple1[T1]], + t1, + ) +} + +// SequenceTuple1 converts a [T.Tuple1[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple1[T1]]] +func SequenceTuple1[ + G_TUPLE1 ~func() ET.Either[E, T.Tuple1[T1]], + G_T1 ~func() ET.Either[E, T1], + E, T1 any](t T.Tuple1[G_T1]) G_TUPLE1 { + return A.SequenceTuple1( + Map[G_T1, G_TUPLE1, E, T1, T.Tuple1[T1]], + t) +} + +// TraverseTuple1 converts a [T.Tuple1[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple1[T1]]] +func TraverseTuple1[ + G_TUPLE1 ~func() ET.Either[E, T.Tuple1[T1]], + F1 ~func(A1) G_T1, + G_T1 ~func() ET.Either[E, T1], + E, A1, T1 any](f1 F1) func(T.Tuple1[A1]) G_TUPLE1 { + return func(t T.Tuple1[A1]) G_TUPLE1 { + return A.TraverseTuple1( + Map[G_T1, G_TUPLE1, E, T1, T.Tuple1[T1]], + f1, + t) + } +} + +// Eitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning a [GIOA] +func Eitherize2[GIOA ~func() ET.Either[error, R], F ~func(T1, T2) (R, error), T1, T2, R any](f F) func(T1, T2) GIOA { + e := ET.Eitherize2(f) + return func(t1 T1, t2 T2) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2) + } + } +} + +// Uneitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning a [GIOA] +func Uneitherize2[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2) GIOA, T1, T2, R any](f GTA) func(T1, T2) (R, error) { + return func(t1 T1, t2 T2) (R, error) { + return ET.Unwrap(f(t1, t2)()) + } +} + +// SequenceT2 converts 2 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple2[T1, T2]]] +func SequenceT2[ + G_TUPLE2 ~func() ET.Either[E, T.Tuple2[T1, T2]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + E, T1, T2 any]( + t1 G_T1, + t2 G_T2, +) G_TUPLE2 { + return A.SequenceT2( + Map[G_T1, func() ET.Either[E, func(T2) T.Tuple2[T1, T2]], E, T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() ET.Either[E, func(T2) T.Tuple2[T1, T2]], G_T2], + t1, + t2, + ) +} + +// SequenceTuple2 converts a [T.Tuple2[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple2[T1, T2]]] +func SequenceTuple2[ + G_TUPLE2 ~func() ET.Either[E, T.Tuple2[T1, T2]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + E, T1, T2 any](t T.Tuple2[G_T1, G_T2]) G_TUPLE2 { + return A.SequenceTuple2( + Map[G_T1, func() ET.Either[E, func(T2) T.Tuple2[T1, T2]], E, T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() ET.Either[E, func(T2) T.Tuple2[T1, T2]], G_T2], + t) +} + +// TraverseTuple2 converts a [T.Tuple2[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple2[T1, T2]]] +func TraverseTuple2[ + G_TUPLE2 ~func() ET.Either[E, T.Tuple2[T1, T2]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + E, A1, A2, T1, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) G_TUPLE2 { + return func(t T.Tuple2[A1, A2]) G_TUPLE2 { + return A.TraverseTuple2( + Map[G_T1, func() ET.Either[E, func(T2) T.Tuple2[T1, T2]], E, T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() ET.Either[E, func(T2) T.Tuple2[T1, T2]], G_T2], + f1, + f2, + t) + } +} + +// Eitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning a [GIOA] +func Eitherize3[GIOA ~func() ET.Either[error, R], F ~func(T1, T2, T3) (R, error), T1, T2, T3, R any](f F) func(T1, T2, T3) GIOA { + e := ET.Eitherize3(f) + return func(t1 T1, t2 T2, t3 T3) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2, t3) + } + } +} + +// Uneitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning a [GIOA] +func Uneitherize3[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2, T3) GIOA, T1, T2, T3, R any](f GTA) func(T1, T2, T3) (R, error) { + return func(t1 T1, t2 T2, t3 T3) (R, error) { + return ET.Unwrap(f(t1, t2, t3)()) + } +} + +// SequenceT3 converts 3 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple3[T1, T2, T3]]] +func SequenceT3[ + G_TUPLE3 ~func() ET.Either[E, T.Tuple3[T1, T2, T3]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + E, T1, T2, T3 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, +) G_TUPLE3 { + return A.SequenceT3( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) T.Tuple3[T1, T2, T3]], E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() ET.Either[E, func(T3) T.Tuple3[T1, T2, T3]], func() ET.Either[E, func(T2) func(T3) T.Tuple3[T1, T2, T3]], G_T2], + Ap[G_TUPLE3, func() ET.Either[E, func(T3) T.Tuple3[T1, T2, T3]], G_T3], + t1, + t2, + t3, + ) +} + +// SequenceTuple3 converts a [T.Tuple3[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple3[T1, T2, T3]]] +func SequenceTuple3[ + G_TUPLE3 ~func() ET.Either[E, T.Tuple3[T1, T2, T3]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + E, T1, T2, T3 any](t T.Tuple3[G_T1, G_T2, G_T3]) G_TUPLE3 { + return A.SequenceTuple3( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) T.Tuple3[T1, T2, T3]], E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() ET.Either[E, func(T3) T.Tuple3[T1, T2, T3]], func() ET.Either[E, func(T2) func(T3) T.Tuple3[T1, T2, T3]], G_T2], + Ap[G_TUPLE3, func() ET.Either[E, func(T3) T.Tuple3[T1, T2, T3]], G_T3], + t) +} + +// TraverseTuple3 converts a [T.Tuple3[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple3[T1, T2, T3]]] +func TraverseTuple3[ + G_TUPLE3 ~func() ET.Either[E, T.Tuple3[T1, T2, T3]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + E, A1, A2, A3, T1, T2, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) G_TUPLE3 { + return func(t T.Tuple3[A1, A2, A3]) G_TUPLE3 { + return A.TraverseTuple3( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) T.Tuple3[T1, T2, T3]], E, T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() ET.Either[E, func(T3) T.Tuple3[T1, T2, T3]], func() ET.Either[E, func(T2) func(T3) T.Tuple3[T1, T2, T3]], G_T2], + Ap[G_TUPLE3, func() ET.Either[E, func(T3) T.Tuple3[T1, T2, T3]], G_T3], + f1, + f2, + f3, + t) + } +} + +// Eitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning a [GIOA] +func Eitherize4[GIOA ~func() ET.Either[error, R], F ~func(T1, T2, T3, T4) (R, error), T1, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) GIOA { + e := ET.Eitherize4(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2, t3, t4) + } + } +} + +// Uneitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning a [GIOA] +func Uneitherize4[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2, T3, T4) GIOA, T1, T2, T3, T4, R any](f GTA) func(T1, T2, T3, T4) (R, error) { + return func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error) { + return ET.Unwrap(f(t1, t2, t3, t4)()) + } +} + +// SequenceT4 converts 4 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple4[T1, T2, T3, T4]]] +func SequenceT4[ + G_TUPLE4 ~func() ET.Either[E, T.Tuple4[T1, T2, T3, T4]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + E, T1, T2, T3, T4 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, +) G_TUPLE4 { + return A.SequenceT4( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() ET.Either[E, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func() ET.Either[E, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T2], + Ap[func() ET.Either[E, func(T4) T.Tuple4[T1, T2, T3, T4]], func() ET.Either[E, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T3], + Ap[G_TUPLE4, func() ET.Either[E, func(T4) T.Tuple4[T1, T2, T3, T4]], G_T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceTuple4 converts a [T.Tuple4[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple4[T1, T2, T3, T4]]] +func SequenceTuple4[ + G_TUPLE4 ~func() ET.Either[E, T.Tuple4[T1, T2, T3, T4]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + E, T1, T2, T3, T4 any](t T.Tuple4[G_T1, G_T2, G_T3, G_T4]) G_TUPLE4 { + return A.SequenceTuple4( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() ET.Either[E, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func() ET.Either[E, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T2], + Ap[func() ET.Either[E, func(T4) T.Tuple4[T1, T2, T3, T4]], func() ET.Either[E, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T3], + Ap[G_TUPLE4, func() ET.Either[E, func(T4) T.Tuple4[T1, T2, T3, T4]], G_T4], + t) +} + +// TraverseTuple4 converts a [T.Tuple4[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple4[T1, T2, T3, T4]]] +func TraverseTuple4[ + G_TUPLE4 ~func() ET.Either[E, T.Tuple4[T1, T2, T3, T4]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + E, A1, A2, A3, A4, T1, T2, T3, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) G_TUPLE4 { + return func(t T.Tuple4[A1, A2, A3, A4]) G_TUPLE4 { + return A.TraverseTuple4( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], E, T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() ET.Either[E, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func() ET.Either[E, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T2], + Ap[func() ET.Either[E, func(T4) T.Tuple4[T1, T2, T3, T4]], func() ET.Either[E, func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T3], + Ap[G_TUPLE4, func() ET.Either[E, func(T4) T.Tuple4[T1, T2, T3, T4]], G_T4], + f1, + f2, + f3, + f4, + t) + } +} + +// Eitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning a [GIOA] +func Eitherize5[GIOA ~func() ET.Either[error, R], F ~func(T1, T2, T3, T4, T5) (R, error), T1, T2, T3, T4, T5, R any](f F) func(T1, T2, T3, T4, T5) GIOA { + e := ET.Eitherize5(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2, t3, t4, t5) + } + } +} + +// Uneitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning a [GIOA] +func Uneitherize5[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2, T3, T4, T5) GIOA, T1, T2, T3, T4, T5, R any](f GTA) func(T1, T2, T3, T4, T5) (R, error) { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) (R, error) { + return ET.Unwrap(f(t1, t2, t3, t4, t5)()) + } +} + +// SequenceT5 converts 5 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceT5[ + G_TUPLE5 ~func() ET.Either[E, T.Tuple5[T1, T2, T3, T4, T5]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + E, T1, T2, T3, T4, T5 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, +) G_TUPLE5 { + return A.SequenceT5( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T3], + Ap[func() ET.Either[E, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T4], + Ap[G_TUPLE5, func() ET.Either[E, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceTuple5 converts a [T.Tuple5[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceTuple5[ + G_TUPLE5 ~func() ET.Either[E, T.Tuple5[T1, T2, T3, T4, T5]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + E, T1, T2, T3, T4, T5 any](t T.Tuple5[G_T1, G_T2, G_T3, G_T4, G_T5]) G_TUPLE5 { + return A.SequenceTuple5( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T3], + Ap[func() ET.Either[E, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T4], + Ap[G_TUPLE5, func() ET.Either[E, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T5], + t) +} + +// TraverseTuple5 converts a [T.Tuple5[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple5[T1, T2, T3, T4, T5]]] +func TraverseTuple5[ + G_TUPLE5 ~func() ET.Either[E, T.Tuple5[T1, T2, T3, T4, T5]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + E, A1, A2, A3, A4, A5, T1, T2, T3, T4, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) G_TUPLE5 { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) G_TUPLE5 { + return A.TraverseTuple5( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], E, T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T3], + Ap[func() ET.Either[E, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() ET.Either[E, func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T4], + Ap[G_TUPLE5, func() ET.Either[E, func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T5], + f1, + f2, + f3, + f4, + f5, + t) + } +} + +// Eitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning a [GIOA] +func Eitherize6[GIOA ~func() ET.Either[error, R], F ~func(T1, T2, T3, T4, T5, T6) (R, error), T1, T2, T3, T4, T5, T6, R any](f F) func(T1, T2, T3, T4, T5, T6) GIOA { + e := ET.Eitherize6(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2, t3, t4, t5, t6) + } + } +} + +// Uneitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning a [GIOA] +func Uneitherize6[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2, T3, T4, T5, T6) GIOA, T1, T2, T3, T4, T5, T6, R any](f GTA) func(T1, T2, T3, T4, T5, T6) (R, error) { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) (R, error) { + return ET.Unwrap(f(t1, t2, t3, t4, t5, t6)()) + } +} + +// SequenceT6 converts 6 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceT6[ + G_TUPLE6 ~func() ET.Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + E, T1, T2, T3, T4, T5, T6 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, +) G_TUPLE6 { + return A.SequenceT6( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T4], + Ap[func() ET.Either[E, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T5], + Ap[G_TUPLE6, func() ET.Either[E, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceTuple6 converts a [T.Tuple6[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceTuple6[ + G_TUPLE6 ~func() ET.Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + E, T1, T2, T3, T4, T5, T6 any](t T.Tuple6[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6]) G_TUPLE6 { + return A.SequenceTuple6( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T4], + Ap[func() ET.Either[E, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T5], + Ap[G_TUPLE6, func() ET.Either[E, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T6], + t) +} + +// TraverseTuple6 converts a [T.Tuple6[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func TraverseTuple6[ + G_TUPLE6 ~func() ET.Either[E, T.Tuple6[T1, T2, T3, T4, T5, T6]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + E, A1, A2, A3, A4, A5, A6, T1, T2, T3, T4, T5, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) G_TUPLE6 { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) G_TUPLE6 { + return A.TraverseTuple6( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T4], + Ap[func() ET.Either[E, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() ET.Either[E, func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T5], + Ap[G_TUPLE6, func() ET.Either[E, func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T6], + f1, + f2, + f3, + f4, + f5, + f6, + t) + } +} + +// Eitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning a [GIOA] +func Eitherize7[GIOA ~func() ET.Either[error, R], F ~func(T1, T2, T3, T4, T5, T6, T7) (R, error), T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T1, T2, T3, T4, T5, T6, T7) GIOA { + e := ET.Eitherize7(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2, t3, t4, t5, t6, t7) + } + } +} + +// Uneitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning a [GIOA] +func Uneitherize7[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2, T3, T4, T5, T6, T7) GIOA, T1, T2, T3, T4, T5, T6, T7, R any](f GTA) func(T1, T2, T3, T4, T5, T6, T7) (R, error) { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) (R, error) { + return ET.Unwrap(f(t1, t2, t3, t4, t5, t6, t7)()) + } +} + +// SequenceT7 converts 7 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceT7[ + G_TUPLE7 ~func() ET.Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + E, T1, T2, T3, T4, T5, T6, T7 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, +) G_TUPLE7 { + return A.SequenceT7( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T5], + Ap[func() ET.Either[E, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T6], + Ap[G_TUPLE7, func() ET.Either[E, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceTuple7 converts a [T.Tuple7[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceTuple7[ + G_TUPLE7 ~func() ET.Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + E, T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7]) G_TUPLE7 { + return A.SequenceTuple7( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T5], + Ap[func() ET.Either[E, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T6], + Ap[G_TUPLE7, func() ET.Either[E, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T7], + t) +} + +// TraverseTuple7 converts a [T.Tuple7[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func TraverseTuple7[ + G_TUPLE7 ~func() ET.Either[E, T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + E, A1, A2, A3, A4, A5, A6, A7, T1, T2, T3, T4, T5, T6, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) G_TUPLE7 { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) G_TUPLE7 { + return A.TraverseTuple7( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T5], + Ap[func() ET.Either[E, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() ET.Either[E, func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T6], + Ap[G_TUPLE7, func() ET.Either[E, func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t) + } +} + +// Eitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning a [GIOA] +func Eitherize8[GIOA ~func() ET.Either[error, R], F ~func(T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8) GIOA { + e := ET.Eitherize8(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2, t3, t4, t5, t6, t7, t8) + } + } +} + +// Uneitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning a [GIOA] +func Uneitherize8[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2, T3, T4, T5, T6, T7, T8) GIOA, T1, T2, T3, T4, T5, T6, T7, T8, R any](f GTA) func(T1, T2, T3, T4, T5, T6, T7, T8) (R, error) { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) (R, error) { + return ET.Unwrap(f(t1, t2, t3, t4, t5, t6, t7, t8)()) + } +} + +// SequenceT8 converts 8 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceT8[ + G_TUPLE8 ~func() ET.Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + E, T1, T2, T3, T4, T5, T6, T7, T8 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, +) G_TUPLE8 { + return A.SequenceT8( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T6], + Ap[func() ET.Either[E, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T7], + Ap[G_TUPLE8, func() ET.Either[E, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceTuple8 converts a [T.Tuple8[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceTuple8[ + G_TUPLE8 ~func() ET.Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + E, T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8]) G_TUPLE8 { + return A.SequenceTuple8( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T6], + Ap[func() ET.Either[E, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T7], + Ap[G_TUPLE8, func() ET.Either[E, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T8], + t) +} + +// TraverseTuple8 converts a [T.Tuple8[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func TraverseTuple8[ + G_TUPLE8 ~func() ET.Either[E, T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + E, A1, A2, A3, A4, A5, A6, A7, A8, T1, T2, T3, T4, T5, T6, T7, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) G_TUPLE8 { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) G_TUPLE8 { + return A.TraverseTuple8( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T6], + Ap[func() ET.Either[E, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() ET.Either[E, func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T7], + Ap[G_TUPLE8, func() ET.Either[E, func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t) + } +} + +// Eitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning a [GIOA] +func Eitherize9[GIOA ~func() ET.Either[error, R], F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9) GIOA { + e := ET.Eitherize9(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2, t3, t4, t5, t6, t7, t8, t9) + } + } +} + +// Uneitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning a [GIOA] +func Uneitherize9[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9) GIOA, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f GTA) func(T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) (R, error) { + return ET.Unwrap(f(t1, t2, t3, t4, t5, t6, t7, t8, t9)()) + } +} + +// SequenceT9 converts 9 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceT9[ + G_TUPLE9 ~func() ET.Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + G_T9 ~func() ET.Either[E, T9], + E, T1, T2, T3, T4, T5, T6, T7, T8, T9 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, + t9 G_T9, +) G_TUPLE9 { + return A.SequenceT9( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T6], + Ap[func() ET.Either[E, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T7], + Ap[func() ET.Either[E, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T8], + Ap[G_TUPLE9, func() ET.Either[E, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceTuple9 converts a [T.Tuple9[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceTuple9[ + G_TUPLE9 ~func() ET.Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + G_T9 ~func() ET.Either[E, T9], + E, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8, G_T9]) G_TUPLE9 { + return A.SequenceTuple9( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T6], + Ap[func() ET.Either[E, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T7], + Ap[func() ET.Either[E, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T8], + Ap[G_TUPLE9, func() ET.Either[E, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T9], + t) +} + +// TraverseTuple9 converts a [T.Tuple9[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func TraverseTuple9[ + G_TUPLE9 ~func() ET.Either[E, T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + F9 ~func(A9) G_T9, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + G_T9 ~func() ET.Either[E, T9], + E, A1, A2, A3, A4, A5, A6, A7, A8, A9, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) G_TUPLE9 { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) G_TUPLE9 { + return A.TraverseTuple9( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T6], + Ap[func() ET.Either[E, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T7], + Ap[func() ET.Either[E, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() ET.Either[E, func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T8], + Ap[G_TUPLE9, func() ET.Either[E, func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t) + } +} + +// Eitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning a [GIOA] +func Eitherize10[GIOA ~func() ET.Either[error, R], F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) GIOA { + e := ET.Eitherize10(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) GIOA { + return func() ET.Either[error, R] { + return e(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) + } + } +} + +// Uneitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning a [GIOA] +func Uneitherize10[GIOA ~func() ET.Either[error, R], GTA ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) GIOA, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f GTA) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) (R, error) { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) (R, error) { + return ET.Unwrap(f(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)()) + } +} + +// SequenceT10 converts 10 [func() ET.Either[E, T]] into a [func() ET.Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceT10[ + G_TUPLE10 ~func() ET.Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + G_T9 ~func() ET.Either[E, T9], + G_T10 ~func() ET.Either[E, T10], + E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, + t9 G_T9, + t10 G_T10, +) G_TUPLE10 { + return A.SequenceT10( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T6], + Ap[func() ET.Either[E, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T7], + Ap[func() ET.Either[E, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T8], + Ap[func() ET.Either[E, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T9], + Ap[G_TUPLE10, func() ET.Either[E, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceTuple10 converts a [T.Tuple10[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceTuple10[ + G_TUPLE10 ~func() ET.Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + G_T9 ~func() ET.Either[E, T9], + G_T10 ~func() ET.Either[E, T10], + E, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8, G_T9, G_T10]) G_TUPLE10 { + return A.SequenceTuple10( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T6], + Ap[func() ET.Either[E, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T7], + Ap[func() ET.Either[E, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T8], + Ap[func() ET.Either[E, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T9], + Ap[G_TUPLE10, func() ET.Either[E, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T10], + t) +} + +// TraverseTuple10 converts a [T.Tuple10[func() ET.Either[E, T]]] into a [func() ET.Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func TraverseTuple10[ + G_TUPLE10 ~func() ET.Either[E, T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + F9 ~func(A9) G_T9, + F10 ~func(A10) G_T10, + G_T1 ~func() ET.Either[E, T1], + G_T2 ~func() ET.Either[E, T2], + G_T3 ~func() ET.Either[E, T3], + G_T4 ~func() ET.Either[E, T4], + G_T5 ~func() ET.Either[E, T5], + G_T6 ~func() ET.Either[E, T6], + G_T7 ~func() ET.Either[E, T7], + G_T8 ~func() ET.Either[E, T8], + G_T9 ~func() ET.Either[E, T9], + G_T10 ~func() ET.Either[E, T10], + E, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) G_TUPLE10 { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) G_TUPLE10 { + return A.TraverseTuple10( + Map[G_T1, func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], E, T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T2], + Ap[func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T3], + Ap[func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T4], + Ap[func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T5], + Ap[func() ET.Either[E, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T6], + Ap[func() ET.Either[E, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T7], + Ap[func() ET.Either[E, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T8], + Ap[func() ET.Either[E, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() ET.Either[E, func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T9], + Ap[G_TUPLE10, func() ET.Either[E, func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t) + } +} diff --git a/v2/ioeither/generic/ioeither.go b/v2/ioeither/generic/ioeither.go new file mode 100644 index 0000000..76b05b6 --- /dev/null +++ b/v2/ioeither/generic/ioeither.go @@ -0,0 +1,374 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "time" + + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + "github.com/IBM/fp-go/v2/internal/eithert" + FE "github.com/IBM/fp-go/v2/internal/fromeither" + FI "github.com/IBM/fp-go/v2/internal/fromio" + FC "github.com/IBM/fp-go/v2/internal/functor" + IO "github.com/IBM/fp-go/v2/io/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// type IOEither[E, A any] = func() Either[E, A] + +func MakeIO[GA ~func() ET.Either[E, A], E, A any](f GA) GA { + return f +} + +func Left[GA ~func() ET.Either[E, A], E, A any](l E) GA { + return MakeIO(eithert.Left(IO.MonadOf[GA, ET.Either[E, A]], l)) +} + +func Right[GA ~func() ET.Either[E, A], E, A any](r A) GA { + return MakeIO(eithert.Right(IO.MonadOf[GA, ET.Either[E, A]], r)) +} + +func Of[GA ~func() ET.Either[E, A], E, A any](r A) GA { + return Right[GA](r) +} + +func MonadOf[GA ~func() ET.Either[E, A], E, A any](r A) GA { + return Of[GA](r) +} + +func LeftIO[GA ~func() ET.Either[E, A], GE ~func() E, E, A any](ml GE) GA { + return MakeIO(eithert.LeftF(IO.MonadMap[GE, GA, E, ET.Either[E, A]], ml)) +} + +func RightIO[GA ~func() ET.Either[E, A], GR ~func() A, E, A any](mr GR) GA { + return MakeIO(eithert.RightF(IO.MonadMap[GR, GA, A, ET.Either[E, A]], mr)) +} + +func FromEither[GA ~func() ET.Either[E, A], E, A any](e ET.Either[E, A]) GA { + return IO.Of[GA](e) +} + +func FromOption[GA ~func() ET.Either[E, A], E, A any](onNone func() E) func(o O.Option[A]) GA { + return FE.FromOption( + FromEither[GA, E, A], + onNone, + ) +} + +func ChainOptionK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](onNone func() E) func(func(A) O.Option[B]) func(GA) GB { + return FE.ChainOptionK( + MonadChain[GA, GB, E, A, B], + FromEither[GB, E, B], + onNone, + ) +} + +func FromIO[GA ~func() ET.Either[E, A], GR ~func() A, E, A any](mr GR) GA { + return RightIO[GA](mr) +} + +func MonadMap[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, f func(A) B) GB { + return eithert.MonadMap(IO.MonadMap[GA, GB, ET.Either[E, A], ET.Either[E, B]], fa, f) +} + +func Map[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) B) func(GA) GB { + return eithert.Map(IO.Map[GA, GB, ET.Either[E, A], ET.Either[E, B]], f) +} + +func MonadMapTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, b B) GB { + return MonadMap[GA, GB](fa, F.Constant1[A](b)) +} + +func MapTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](b B) func(GA) GB { + return Map[GA, GB](F.Constant1[A](b)) +} + +func MonadChain[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, f func(A) GB) GB { + return eithert.MonadChain(IO.MonadChain[GA, GB, ET.Either[E, A], ET.Either[E, B]], IO.MonadOf[GB, ET.Either[E, B]], fa, f) +} + +func Chain[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) GB) func(GA) GB { + return eithert.Chain(IO.Chain[GA, GB, ET.Either[E, A], ET.Either[E, B]], IO.Of[GB, ET.Either[E, B]], f) +} + +func MonadChainTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fa GA, fb GB) GB { + return MonadChain(fa, F.Constant1[A](fb)) +} + +func ChainTo[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](fb GB) func(GA) GB { + return Chain[GA, GB, E, A, B](F.Constant1[A](fb)) +} + +func MonadChainEitherK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](ma GA, f func(A) ET.Either[E, B]) GB { + return FE.MonadChainEitherK( + MonadChain[GA, GB, E, A, B], + FromEither[GB, E, B], + ma, + f, + ) +} + +func MonadChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~func() B, E, A, B any](ma GA, f func(A) GR) GB { + return FI.MonadChainIOK( + MonadChain[GA, GB, E, A, B], + FromIO[GB, GR, E, B], + ma, + f, + ) +} + +func ChainIOK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GR ~func() B, E, A, B any](f func(A) GR) func(GA) GB { + return FI.ChainIOK( + Chain[GA, GB, E, A, B], + FromIO[GB, GR, E, B], + f, + ) +} + +func ChainEitherK[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) ET.Either[E, B]) func(GA) GB { + return FE.ChainEitherK( + Chain[GA, GB, E, A, B], + FromEither[GB, E, B], + f, + ) +} + +func MonadAp[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](mab GAB, ma GA) GB { + return eithert.MonadAp( + IO.MonadAp[GA, GB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, A], ET.Either[E, B]], + IO.MonadMap[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + mab, ma) +} + +func Ap[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB { + return eithert.Ap( + IO.Ap[GB, func() func(ET.Either[E, A]) ET.Either[E, B], GA, ET.Either[E, B], ET.Either[E, A]], + IO.Map[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + ma) +} + +func MonadApSeq[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](mab GAB, ma GA) GB { + return eithert.MonadAp( + IO.MonadApSeq[GA, GB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, A], ET.Either[E, B]], + IO.MonadMap[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + mab, ma) +} + +func ApSeq[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB { + return eithert.Ap( + IO.ApSeq[GB, func() func(ET.Either[E, A]) ET.Either[E, B], GA, ET.Either[E, B], ET.Either[E, A]], + IO.Map[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + ma) +} + +func MonadApPar[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](mab GAB, ma GA) GB { + return eithert.MonadAp( + IO.MonadApPar[GA, GB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, A], ET.Either[E, B]], + IO.MonadMap[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + mab, ma) +} + +func ApPar[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA ~func() ET.Either[E, A], E, A, B any](ma GA) func(GAB) GB { + return eithert.Ap( + IO.ApPar[GB, func() func(ET.Either[E, A]) ET.Either[E, B], GA, ET.Either[E, B], ET.Either[E, A]], + IO.Map[GAB, func() func(ET.Either[E, A]) ET.Either[E, B], ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + ma) +} + +func Flatten[GA ~func() ET.Either[E, A], GAA ~func() ET.Either[E, GA], E, A any](mma GAA) GA { + return MonadChain(mma, F.Identity[GA]) +} + +func TryCatch[GA ~func() ET.Either[E, A], E, A any](f func() (A, error), onThrow func(error) E) GA { + return MakeIO(func() ET.Either[E, A] { + a, err := f() + return ET.TryCatch(a, err, onThrow) + }) +} + +func TryCatchError[GA ~func() ET.Either[error, A], A any](f func() (A, error)) GA { + return MakeIO(func() ET.Either[error, A] { + return ET.TryCatchError(f()) + }) +} + +// Memoize computes the value of the provided IO monad lazily but exactly once +func Memoize[GA ~func() ET.Either[E, A], E, A any](ma GA) GA { + return IO.Memoize(ma) +} + +func MonadMapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2, A any](fa GA1, f func(E1) E2) GA2 { + return eithert.MonadMapLeft( + IO.MonadMap[GA1, GA2, ET.Either[E1, A], ET.Either[E2, A]], + fa, + f, + ) +} + +func MapLeft[GA1 ~func() ET.Either[E1, A], GA2 ~func() ET.Either[E2, A], E1, E2, A any](f func(E1) E2) func(GA1) GA2 { + return eithert.MapLeft( + IO.Map[GA1, GA2, ET.Either[E1, A], ET.Either[E2, A]], + f, + ) +} + +// Delay creates an operation that passes in the value after some [time.Duration] +func Delay[GA ~func() ET.Either[E, A], E, A any](delay time.Duration) func(GA) GA { + return IO.Delay[GA](delay) +} + +// After creates an operation that passes after the given [time.Time] +func After[GA ~func() ET.Either[E, A], E, A any](timestamp time.Time) func(GA) GA { + return IO.After[GA](timestamp) +} + +func MonadBiMap[GA ~func() ET.Either[E1, A], GB ~func() ET.Either[E2, B], E1, E2, A, B any](fa GA, f func(E1) E2, g func(A) B) GB { + return eithert.MonadBiMap(IO.MonadMap[GA, GB, ET.Either[E1, A], ET.Either[E2, B]], fa, f, g) +} + +// BiMap maps a pair of functions over the two type arguments of the bifunctor. +func BiMap[GA ~func() ET.Either[E1, A], GB ~func() ET.Either[E2, B], E1, E2, A, B any](f func(E1) E2, g func(A) B) func(GA) GB { + return eithert.BiMap(IO.Map[GA, GB, ET.Either[E1, A], ET.Either[E2, B]], f, g) +} + +// Fold convers an IOEither into an IO +func Fold[GA ~func() ET.Either[E, A], GB ~func() B, E, A, B any](onLeft func(E) GB, onRight func(A) GB) func(GA) GB { + return eithert.MatchE(IO.MonadChain[GA, GB, ET.Either[E, A], B], onLeft, onRight) +} + +func MonadFold[GA ~func() ET.Either[E, A], GB ~func() B, E, A, B any](ma GA, onLeft func(E) GB, onRight func(A) GB) GB { + return eithert.FoldE(IO.MonadChain[GA, GB, ET.Either[E, A], B], ma, onLeft, onRight) +} + +// GetOrElse extracts the value or maps the error +func GetOrElse[GA ~func() ET.Either[E, A], GB ~func() A, E, A any](onLeft func(E) GB) func(GA) GB { + return eithert.GetOrElse(IO.MonadChain[GA, GB, ET.Either[E, A], A], IO.MonadOf[GB, A], onLeft) +} + +// MonadChainFirst runs the monad returned by the function but returns the result of the original monad +func MonadChainFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](ma GA, f func(A) GB) GA { + return C.MonadChainFirst( + MonadChain[GA, GA, E, A, A], + MonadMap[GB, GA, E, B, A], + ma, + f, + ) +} + +// ChainFirst runs the monad returned by the function but returns the result of the original monad +func ChainFirst[GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], E, A, B any](f func(A) GB) func(GA) GA { + return C.ChainFirst( + Chain[GA, GA, E, A, A], + Map[GB, GA, E, B, A], + f, + ) +} + +// MonadChainFirstIOK runs the monad returned by the function but returns the result of the original monad +func MonadChainFirstIOK[GA ~func() ET.Either[E, A], GIOB ~func() B, E, A, B any](first GA, f func(A) GIOB) GA { + return FI.MonadChainFirstIOK( + MonadChain[GA, GA, E, A, A], + MonadMap[func() ET.Either[E, B], GA, E, B, A], + FromIO[func() ET.Either[E, B], GIOB, E, B], + first, + f, + ) +} + +// ChainFirstIOK runs the monad returned by the function but returns the result of the original monad +func ChainFirstIOK[GA ~func() ET.Either[E, A], GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GA) GA { + return FI.ChainFirstIOK( + Chain[GA, GA, E, A, A], + Map[func() ET.Either[E, B], GA, E, B, A], + FromIO[func() ET.Either[E, B], GIOB, E, B], + f, + ) +} + +// MonadChainFirstEitherK runs the monad returned by the function but returns the result of the original monad +func MonadChainFirstEitherK[GA ~func() ET.Either[E, A], E, A, B any](first GA, f func(A) ET.Either[E, B]) GA { + return FE.MonadChainFirstEitherK( + MonadChain[GA, GA, E, A, A], + MonadMap[func() ET.Either[E, B], GA, E, B, A], + FromEither[func() ET.Either[E, B], E, B], + first, + f, + ) +} + +// ChainFirstEitherK runs the monad returned by the function but returns the result of the original monad +func ChainFirstEitherK[GA ~func() ET.Either[E, A], E, A, B any](f func(A) ET.Either[E, B]) func(GA) GA { + return FE.ChainFirstEitherK( + Chain[GA, GA, E, A, A], + Map[func() ET.Either[E, B], GA, E, B, A], + FromEither[func() ET.Either[E, B], E, B], + f, + ) +} + +// Swap changes the order of type parameters +func Swap[GEA ~func() ET.Either[E, A], GAE ~func() ET.Either[A, E], E, A any](val GEA) GAE { + return MonadFold(val, Right[GAE], Left[GAE]) +} + +// FromImpure converts a side effect without a return value into a side effect that returns any +func FromImpure[GA ~func() ET.Either[E, any], IMP ~func(), E any](f IMP) GA { + return F.Pipe2( + f, + IO.FromImpure[func() any, IMP], + FromIO[GA, func() any], + ) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[GEA ~func() ET.Either[E, A], E, A any](gen func() GEA) GEA { + return IO.Defer[GEA](gen) +} + +func MonadAlt[LAZY ~func() GIOA, GIOA ~func() ET.Either[E, A], E, A any](first GIOA, second LAZY) GIOA { + return eithert.MonadAlt( + IO.Of[GIOA], + IO.MonadChain[GIOA, GIOA], + + first, + second, + ) +} + +func Alt[LAZY ~func() GIOA, GIOA ~func() ET.Either[E, A], E, A any](second LAZY) func(GIOA) GIOA { + return F.Bind2nd(MonadAlt[LAZY], second) +} + +func MonadFlap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], E, B, A any](fab GEAB, a A) GEB { + return FC.MonadFlap(MonadMap[GEAB, GEB], fab, a) +} + +func Flap[GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], E, B, A any](a A) func(GEAB) GEB { + return FC.Flap(Map[GEAB, GEB], a) +} + +func ToIOOption[GA ~func() O.Option[A], GEA ~func() ET.Either[E, A], E, A any](ioe GEA) GA { + return F.Pipe1( + ioe, + IO.Map[GEA, GA](ET.ToOption[E, A]), + ) +} + +func FromIOOption[GEA ~func() ET.Either[E, A], GA ~func() O.Option[A], E, A any](onNone func() E) func(ioo GA) GEA { + return IO.Map[GA, GEA](ET.FromOption[A](onNone)) +} diff --git a/v2/ioeither/generic/logging.go b/v2/ioeither/generic/logging.go new file mode 100644 index 0000000..4c2f9dd --- /dev/null +++ b/v2/ioeither/generic/logging.go @@ -0,0 +1,51 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "encoding/json" + "log" + + B "github.com/IBM/fp-go/v2/bytes" + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" +) + +// LogJSON converts the argument to JSON and then logs it via the format string +// Can be used with [ChainFirst] +func LogJSON[GA ~func() ET.Either[error, any], A any](prefix string) func(A) GA { + return func(a A) GA { + // log this + return F.Pipe3( + ET.TryCatchError(json.MarshalIndent(a, "", " ")), + ET.Map[error](B.ToString), + FromEither[func() ET.Either[error, string]], + Chain[func() ET.Either[error, string], GA](func(data string) GA { + return FromImpure[GA](func() { + log.Printf(prefix, data) + }) + }), + ) + } +} + +// LogJson converts the argument to JSON and then logs it via the format string +// Can be used with [ChainFirst] +// +// Deprecated: use [LogJSON] instead +func LogJson[GA ~func() ET.Either[error, any], A any](prefix string) func(A) GA { + return LogJSON[GA, A](prefix) +} diff --git a/v2/ioeither/generic/monad.go b/v2/ioeither/generic/monad.go new file mode 100644 index 0000000..4385358 --- /dev/null +++ b/v2/ioeither/generic/monad.go @@ -0,0 +1,68 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" +) + +type ioEitherPointed[E, A any, GA ~func() ET.Either[E, A]] struct{} + +type ioEitherMonad[E, A, B any, GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B]] struct{} + +type ioEitherFunctor[E, A, B any, GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B]] struct{} + +func (o *ioEitherPointed[E, A, GA]) Of(a A) GA { + return Of[GA, E, A](a) +} + +func (o *ioEitherMonad[E, A, B, GA, GB, GAB]) Of(a A) GA { + return Of[GA, E, A](a) +} + +func (o *ioEitherMonad[E, A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB { + return Map[GA, GB, E, A, B](f) +} + +func (o *ioEitherMonad[E, A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB { + return Chain[GA, GB, E, A, B](f) +} + +func (o *ioEitherMonad[E, A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB { + return Ap[GB, GAB, GA, E, A, B](fa) +} + +func (o *ioEitherFunctor[E, A, B, GA, GB]) Map(f func(A) B) func(GA) GB { + return Map[GA, GB, E, A, B](f) +} + +// Pointed implements the pointed operations for [IOEither] +func Pointed[E, A any, GA ~func() ET.Either[E, A]]() pointed.Pointed[A, GA] { + return &ioEitherPointed[E, A, GA]{} +} + +// Functor implements the monadic operations for [IOEither] +func Functor[E, A, B any, GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B]]() functor.Functor[A, B, GA, GB] { + return &ioEitherFunctor[E, A, B, GA, GB]{} +} + +// Monad implements the monadic operations for [IOEither] +func Monad[E, A, B any, GA ~func() ET.Either[E, A], GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B]]() monad.Monad[A, B, GA, GB, GAB] { + return &ioEitherMonad[E, A, B, GA, GB, GAB]{} +} diff --git a/v2/ioeither/generic/monoid.go b/v2/ioeither/generic/monoid.go new file mode 100644 index 0000000..664375c --- /dev/null +++ b/v2/ioeither/generic/monoid.go @@ -0,0 +1,54 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + M "github.com/IBM/fp-go/v2/monoid" +) + +func ApplicativeMonoid[GEA ~func() ET.Either[E, A], GEFA ~func() ET.Either[E, func(A) A], E, A any]( + m M.Monoid[A], +) M.Monoid[GEA] { + return M.ApplicativeMonoid( + MonadOf[GEA], + MonadMap[GEA, GEFA], + MonadAp[GEA, GEFA, GEA], + m, + ) +} + +func ApplicativeMonoidSeq[GEA ~func() ET.Either[E, A], GEFA ~func() ET.Either[E, func(A) A], E, A any]( + m M.Monoid[A], +) M.Monoid[GEA] { + return M.ApplicativeMonoid( + MonadOf[GEA], + MonadMap[GEA, GEFA], + MonadApSeq[GEA, GEFA, GEA], + m, + ) +} + +func ApplicativeMonoidPar[GEA ~func() ET.Either[E, A], GEFA ~func() ET.Either[E, func(A) A], E, A any]( + m M.Monoid[A], +) M.Monoid[GEA] { + return M.ApplicativeMonoid( + MonadOf[GEA], + MonadMap[GEA, GEFA], + MonadApPar[GEA, GEFA, GEA], + m, + ) +} diff --git a/v2/ioeither/generic/resource.go b/v2/ioeither/generic/resource.go new file mode 100644 index 0000000..3bd8883 --- /dev/null +++ b/v2/ioeither/generic/resource.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IF "github.com/IBM/fp-go/v2/internal/file" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[ + GA ~func() ET.Either[E, A], + GR ~func() ET.Either[E, R], + GANY ~func() ET.Either[E, ANY], + E, R, A, ANY any](onCreate GR, onRelease func(R) GANY) func(func(R) GA) GA { + return IF.WithResource( + MonadChain[GR, GA, E, R, A], + MonadFold[GA, GA, E, A, ET.Either[E, A]], + MonadFold[GANY, GA, E, ANY, ET.Either[E, A]], + MonadMap[GANY, GA, E, ANY, A], + Left[GA, E, A], + )(F.Constant(onCreate), onRelease) +} diff --git a/v2/ioeither/generic/retry.go b/v2/ioeither/generic/retry.go new file mode 100644 index 0000000..8bb2319 --- /dev/null +++ b/v2/ioeither/generic/retry.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + GIO "github.com/IBM/fp-go/v2/io/generic" + R "github.com/IBM/fp-go/v2/retry" +) + +// Retry combinator for actions that don't raise exceptions, but +// signal in their type the outcome has failed. Examples are the +// `Option`, `Either` and `EitherT` monads. +// +// policy - refers to the retry policy +// action - converts a status into an operation to be executed +// check - checks if the result of the action needs to be retried +func Retrying[GA ~func() ET.Either[E, A], E, A any]( + policy R.RetryPolicy, + action func(R.RetryStatus) GA, + check func(ET.Either[E, A]) bool, +) GA { + // get an implementation for the types + return GIO.Retrying(policy, action, check) +} diff --git a/v2/ioeither/generic/semigroup.go b/v2/ioeither/generic/semigroup.go new file mode 100644 index 0000000..0b6bd33 --- /dev/null +++ b/v2/ioeither/generic/semigroup.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func AltSemigroup[GIOA ~func() ET.Either[E, A], E, A any]() S.Semigroup[GIOA] { + return S.AltSemigroup( + MonadAlt[func() GIOA], + ) +} diff --git a/v2/ioeither/generic/sync.go b/v2/ioeither/generic/sync.go new file mode 100644 index 0000000..14e1008 --- /dev/null +++ b/v2/ioeither/generic/sync.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/io/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[GA ~func() ET.Either[E, A], E, A any](lock func() context.CancelFunc) func(fa GA) GA { + return G.WithLock[GA](lock) +} diff --git a/v2/ioeither/generic/traverse.go b/v2/ioeither/generic/traverse.go new file mode 100644 index 0000000..8dbc45e --- /dev/null +++ b/v2/ioeither/generic/traverse.go @@ -0,0 +1,293 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" + RR "github.com/IBM/fp-go/v2/internal/record" +) + +// MonadTraverseArray transforms an array +func MonadTraverseArray[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(A) GB) GBS { + return RA.MonadTraverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + Ap[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseArray transforms an array +func TraverseArray[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + Ap[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// MonadTraverseArrayWithIndex transforms an array +func MonadTraverseArrayWithIndex[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(int, A) GB) GBS { + return RA.MonadTraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + Ap[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + Ap[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~[]A, GAAS ~[]GA, E, A any](tas GAAS) GAS { + return MonadTraverseArray[GA, GAS](tas, F.Identity[GA]) +} + +// MonadTraverseRecord transforms an array +func MonadTraverseRecord[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](tas AAS, f func(A) GB) GBS { + return RR.MonadTraverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + Ap[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseRecord transforms an array +func TraverseRecord[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(A) GB) func(AAS) GBS { + return RR.Traverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + Ap[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// TraverseRecordWithIndex transforms an array +func TraverseRecordWithIndex[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(K, A) GB) func(AAS) GBS { + return RR.TraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + Ap[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// SequenceRecord converts a homogeneous sequence of either into an either of sequence +func SequenceRecord[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~map[K]A, GAAS ~map[K]GA, K comparable, E, A any](tas GAAS) GAS { + return MonadTraverseRecord[GA, GAS](tas, F.Identity[GA]) +} + +// MonadTraverseArraySeq transforms an array +func MonadTraverseArraySeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(A) GB) GBS { + return RA.MonadTraverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseArraySeq transforms an array +func TraverseArraySeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// MonadTraverseArrayWithIndexSeq transforms an array +func MonadTraverseArrayWithIndexSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(int, A) GB) GBS { + return RA.MonadTraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseArrayWithIndexSeq transforms an array +func TraverseArrayWithIndexSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence +func SequenceArraySeq[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~[]A, GAAS ~[]GA, E, A any](tas GAAS) GAS { + return MonadTraverseArraySeq[GA, GAS](tas, F.Identity[GA]) +} + +// MonadTraverseRecordSeq transforms an array +func MonadTraverseRecordSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](tas AAS, f func(A) GB) GBS { + return RR.MonadTraverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseRecordSeq transforms an array +func TraverseRecordSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(A) GB) func(AAS) GBS { + return RR.Traverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// TraverseRecordWithIndexSeq transforms an array +func TraverseRecordWithIndexSeq[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(K, A) GB) func(AAS) GBS { + return RR.TraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApSeq[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence +func SequenceRecordSeq[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~map[K]A, GAAS ~map[K]GA, K comparable, E, A any](tas GAAS) GAS { + return MonadTraverseRecordSeq[GA, GAS](tas, F.Identity[GA]) +} + +// MonadTraverseArrayPar transforms an array +func MonadTraverseArrayPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(A) GB) GBS { + return RA.MonadTraverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApPar[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseArrayPar transforms an array +func TraverseArrayPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApPar[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// MonadTraverseArrayWithIndexPar transforms an array +func MonadTraverseArrayWithIndexPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](tas AAS, f func(int, A) GB) GBS { + return RA.MonadTraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApPar[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseArrayWithIndexPar transforms an array +func TraverseArrayWithIndexPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~[]A, BBS ~[]B, E, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApPar[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// SequenceArrayPar converts a homogeneous sequence of either into an either of sequence +func SequenceArrayPar[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~[]A, GAAS ~[]GA, E, A any](tas GAAS) GAS { + return MonadTraverseArrayPar[GA, GAS](tas, F.Identity[GA]) +} + +// MonadTraverseRecordPar transforms an array +func MonadTraverseRecordPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](tas AAS, f func(A) GB) GBS { + return RR.MonadTraverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApPar[GBS, func() ET.Either[E, func(B) BBS], GB], + + tas, + f, + ) +} + +// TraverseRecordPar transforms an array +func TraverseRecordPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(A) GB) func(AAS) GBS { + return RR.Traverse[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApPar[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// TraverseRecordWithIndexPar transforms an array +func TraverseRecordWithIndexPar[GB ~func() ET.Either[E, B], GBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, E, A, B any](f func(K, A) GB) func(AAS) GBS { + return RR.TraverseWithIndex[AAS]( + Of[GBS, E, BBS], + Map[GBS, func() ET.Either[E, func(B) BBS], E, BBS, func(B) BBS], + ApPar[GBS, func() ET.Either[E, func(B) BBS], GB], + + f, + ) +} + +// SequenceRecordPar converts a homogeneous sequence of either into an either of sequence +func SequenceRecordPar[GA ~func() ET.Either[E, A], GAS ~func() ET.Either[E, AAS], AAS ~map[K]A, GAAS ~map[K]GA, K comparable, E, A any](tas GAAS) GAS { + return MonadTraverseRecordPar[GA, GAS](tas, F.Identity[GA]) +} diff --git a/v2/ioeither/http/builder/builder.go b/v2/ioeither/http/builder/builder.go new file mode 100644 index 0000000..6bc3b6e --- /dev/null +++ b/v2/ioeither/http/builder/builder.go @@ -0,0 +1,67 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package builder + +import ( + "bytes" + "net/http" + "strconv" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + R "github.com/IBM/fp-go/v2/http/builder" + H "github.com/IBM/fp-go/v2/http/headers" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOEH "github.com/IBM/fp-go/v2/ioeither/http" + LZ "github.com/IBM/fp-go/v2/lazy" + O "github.com/IBM/fp-go/v2/option" +) + +func Requester(builder *R.Builder) IOEH.Requester { + + withBody := F.Curry3(func(data []byte, url string, method string) IOE.IOEither[error, *http.Request] { + return IOE.TryCatchError(func() (*http.Request, error) { + req, err := http.NewRequest(method, url, bytes.NewReader(data)) + if err == nil { + req.Header.Set(H.ContentLength, strconv.Itoa(len(data))) + H.Monoid.Concat(req.Header, builder.GetHeaders()) + } + return req, err + }) + }) + + withoutBody := F.Curry2(func(url string, method string) IOE.IOEither[error, *http.Request] { + return IOE.TryCatchError(func() (*http.Request, error) { + req, err := http.NewRequest(method, url, nil) + if err == nil { + H.Monoid.Concat(req.Header, builder.GetHeaders()) + } + return req, err + }) + }) + + return F.Pipe5( + builder.GetBody(), + O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)), + E.Ap[func(string) IOE.IOEither[error, *http.Request]](builder.GetTargetURL()), + E.Flap[error, IOE.IOEither[error, *http.Request]](builder.GetMethod()), + E.GetOrElse(IOE.Left[*http.Request, error]), + IOE.Map[error](func(req *http.Request) *http.Request { + req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders()) + return req + }), + ) +} diff --git a/v2/ioeither/http/builder/builder_test.go b/v2/ioeither/http/builder/builder_test.go new file mode 100644 index 0000000..2dea752 --- /dev/null +++ b/v2/ioeither/http/builder/builder_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package builder + +import ( + "net/http" + "net/url" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + R "github.com/IBM/fp-go/v2/http/builder" + IO "github.com/IBM/fp-go/v2/io" + IOE "github.com/IBM/fp-go/v2/ioeither" + "github.com/stretchr/testify/assert" +) + +func TestBuilderWithQuery(t *testing.T) { + // add some query + withLimit := R.WithQueryArg("limit")("10") + withURL := R.WithUrl("http://www.example.org?a=b") + + b := F.Pipe2( + R.Default, + withLimit, + withURL, + ) + + req := F.Pipe3( + b, + Requester, + IOE.Map[error](func(r *http.Request) *url.URL { + return r.URL + }), + IOE.ChainFirstIOK[error](func(u *url.URL) IO.IO[any] { + return IO.FromImpure(func() { + q := u.Query() + assert.Equal(t, "10", q.Get("limit")) + assert.Equal(t, "b", q.Get("a")) + }) + }), + ) + + assert.True(t, E.IsRight(req())) +} diff --git a/v2/ioeither/http/di/di.go b/v2/ioeither/http/di/di.go new file mode 100644 index 0000000..40fbd41 --- /dev/null +++ b/v2/ioeither/http/di/di.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package di + +import ( + "net/http" + + DI "github.com/IBM/fp-go/v2/di" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOEH "github.com/IBM/fp-go/v2/ioeither/http" +) + +var ( + // InjHttpClient is the [DI.InjectionToken] for the [http.DefaultClient] + InjHttpClient = DI.MakeTokenWithDefault0("HTTP_CLIENT", IOE.Of[error](http.DefaultClient)) + + // InjClient is the [DI.InjectionToken] for the default [IOEH.Client] + InjClient = DI.MakeTokenWithDefault1("CLIENT", InjHttpClient.IOEither(), IOE.Map[error](IOEH.MakeClient)) +) diff --git a/v2/ioeither/http/request.go b/v2/ioeither/http/request.go new file mode 100644 index 0000000..558cbe9 --- /dev/null +++ b/v2/ioeither/http/request.go @@ -0,0 +1,145 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "bytes" + "io" + "net/http" + + B "github.com/IBM/fp-go/v2/bytes" + FL "github.com/IBM/fp-go/v2/file" + F "github.com/IBM/fp-go/v2/function" + H "github.com/IBM/fp-go/v2/http" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOEF "github.com/IBM/fp-go/v2/ioeither/file" + J "github.com/IBM/fp-go/v2/json" + P "github.com/IBM/fp-go/v2/pair" +) + +type ( + // Requester is a reader that constructs a request + Requester = IOE.IOEither[error, *http.Request] + + Client interface { + Do(Requester) IOE.IOEither[error, *http.Response] + } + + client struct { + delegate *http.Client + doIOE func(*http.Request) IOE.IOEither[error, *http.Response] + } +) + +var ( + // MakeRequest is an eitherized version of [http.NewRequest] + MakeRequest = IOE.Eitherize3(http.NewRequest) + makeRequest = F.Bind13of3(MakeRequest) + + // specialize + MakeGetRequest = makeRequest("GET", nil) +) + +// MakeBodyRequest creates a request that carries a body +func MakeBodyRequest(method string, body IOE.IOEither[error, []byte]) func(url string) IOE.IOEither[error, *http.Request] { + onBody := F.Pipe1( + body, + IOE.Map[error](F.Flow2( + bytes.NewReader, + FL.ToReader[*bytes.Reader], + )), + ) + onRelease := IOE.Of[error, io.Reader] + withMethod := F.Bind1of3(MakeRequest)(method) + + return F.Flow2( + F.Bind1of2(withMethod), + IOE.WithResource[*http.Request](onBody, onRelease), + ) +} + +func (client client) Do(req Requester) IOE.IOEither[error, *http.Response] { + return F.Pipe1( + req, + IOE.Chain(client.doIOE), + ) +} + +func MakeClient(httpClient *http.Client) Client { + return client{delegate: httpClient, doIOE: IOE.Eitherize1(httpClient.Do)} +} + +// ReadFullResponse sends a request, reads the response as a byte array and represents the result as a tuple +func ReadFullResponse(client Client) func(Requester) IOE.IOEither[error, H.FullResponse] { + return F.Flow3( + client.Do, + IOE.ChainEitherK(H.ValidateResponse), + IOE.Chain(func(resp *http.Response) IOE.IOEither[error, H.FullResponse] { + return F.Pipe1( + F.Pipe3( + resp, + H.GetBody, + IOE.Of[error, io.ReadCloser], + IOEF.ReadAll[io.ReadCloser], + ), + IOE.Map[error](F.Bind1st(P.MakePair[*http.Response, []byte], resp)), + ) + }), + ) +} + +// ReadAll sends a request and reads the response as bytes +func ReadAll(client Client) func(Requester) IOE.IOEither[error, []byte] { + return F.Flow2( + ReadFullResponse(client), + IOE.Map[error](H.Body), + ) +} + +// ReadText sends a request, reads the response and represents the response as a text string +func ReadText(client Client) func(Requester) IOE.IOEither[error, string] { + return F.Flow2( + ReadAll(client), + IOE.Map[error](B.ToString), + ) +} + +// ReadJson sends a request, reads the response and parses the response as JSON +// +// Deprecated: use [ReadJSON] instead +func ReadJson[A any](client Client) func(Requester) IOE.IOEither[error, A] { + return ReadJSON[A](client) +} + +// readJSON sends a request, reads the response and parses the response as a []byte +func readJSON(client Client) func(Requester) IOE.IOEither[error, []byte] { + return F.Flow3( + ReadFullResponse(client), + IOE.ChainFirstEitherK(F.Flow2( + H.Response, + H.ValidateJSONResponse, + )), + IOE.Map[error](H.Body), + ) +} + +// ReadJSON sends a request, reads the response and parses the response as JSON +func ReadJSON[A any](client Client) func(Requester) IOE.IOEither[error, A] { + return F.Flow2( + readJSON(client), + IOE.ChainEitherK[error](J.Unmarshal[A]), + ) +} diff --git a/v2/ioeither/http/retry_test.go b/v2/ioeither/http/retry_test.go new file mode 100644 index 0000000..4c3ab99 --- /dev/null +++ b/v2/ioeither/http/retry_test.go @@ -0,0 +1,71 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "net" + "net/http" + "testing" + "time" + + AR "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + O "github.com/IBM/fp-go/v2/option" + R "github.com/IBM/fp-go/v2/retry" + "github.com/stretchr/testify/assert" +) + +var expLogBackoff = R.ExponentialBackoff(250 * time.Millisecond) + +// our retry policy with a 1s cap +var testLogPolicy = R.CapDelay( + 2*time.Second, + R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)), +) + +type PostItem struct { + UserID uint `json:"userId"` + Id uint `json:"id"` + Title string `json:"title"` + Body string `json:"body"` +} + +func TestRetryHttp(t *testing.T) { + // URLs to try, the first URLs have an invalid hostname + urls := AR.From("https://jsonplaceholder1.typicode.com/posts/1", "https://jsonplaceholder2.typicode.com/posts/1", "https://jsonplaceholder3.typicode.com/posts/1", "https://jsonplaceholder4.typicode.com/posts/1", "https://jsonplaceholder.typicode.com/posts/1") + client := MakeClient(&http.Client{}) + + action := func(status R.RetryStatus) IOE.IOEither[error, *PostItem] { + return F.Pipe1( + MakeGetRequest(urls[status.IterNumber]), + ReadJSON[*PostItem](client), + ) + } + + check := E.Fold( + F.Flow2( + errors.As[*net.DNSError](), + O.IsSome[*net.DNSError], + ), + F.Constant1[*PostItem](false), + ) + + item := IOE.Retrying(testLogPolicy, action, check)() + assert.True(t, E.IsRight(item)) +} diff --git a/v2/ioeither/ioeither.go b/v2/ioeither/ioeither.go new file mode 100644 index 0000000..caea425 --- /dev/null +++ b/v2/ioeither/ioeither.go @@ -0,0 +1,290 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "time" + + ET "github.com/IBM/fp-go/v2/either" + I "github.com/IBM/fp-go/v2/io" + G "github.com/IBM/fp-go/v2/ioeither/generic" + IOO "github.com/IBM/fp-go/v2/iooption" + L "github.com/IBM/fp-go/v2/lazy" + O "github.com/IBM/fp-go/v2/option" +) + +// IOEither represents a synchronous computation that may fail +// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details +type IOEither[E, A any] I.IO[ET.Either[E, A]] + +func MakeIO[E, A any](f IOEither[E, A]) IOEither[E, A] { + return G.MakeIO(f) +} + +func Left[A, E any](l E) IOEither[E, A] { + return G.Left[IOEither[E, A]](l) +} + +func Right[E, A any](r A) IOEither[E, A] { + return G.Right[IOEither[E, A]](r) +} + +func Of[E, A any](r A) IOEither[E, A] { + return G.Of[IOEither[E, A]](r) +} + +func MonadOf[E, A any](r A) IOEither[E, A] { + return G.MonadOf[IOEither[E, A]](r) +} + +func LeftIO[A, E any](ml I.IO[E]) IOEither[E, A] { + return G.LeftIO[IOEither[E, A]](ml) +} + +func RightIO[E, A any](mr I.IO[A]) IOEither[E, A] { + return G.RightIO[IOEither[E, A]](mr) +} + +func FromEither[E, A any](e ET.Either[E, A]) IOEither[E, A] { + return G.FromEither[IOEither[E, A]](e) +} + +func FromOption[A, E any](onNone func() E) func(o O.Option[A]) IOEither[E, A] { + return G.FromOption[IOEither[E, A]](onNone) +} + +func FromIOOption[A, E any](onNone func() E) func(o IOO.IOOption[A]) IOEither[E, A] { + return G.FromIOOption[IOEither[E, A], IOO.IOOption[A]](onNone) +} + +func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(IOEither[E, A]) IOEither[E, B] { + return G.ChainOptionK[IOEither[E, A], IOEither[E, B]](onNone) +} + +func MonadChainIOK[E, A, B any](ma IOEither[E, A], f func(A) I.IO[B]) IOEither[E, B] { + return G.MonadChainIOK[IOEither[E, A], IOEither[E, B]](ma, f) +} + +func ChainIOK[E, A, B any](f func(A) I.IO[B]) func(IOEither[E, A]) IOEither[E, B] { + return G.ChainIOK[IOEither[E, A], IOEither[E, B]](f) +} + +func ChainLazyK[E, A, B any](f func(A) L.Lazy[B]) func(IOEither[E, A]) IOEither[E, B] { + return G.ChainIOK[IOEither[E, A], IOEither[E, B]](f) +} + +// FromIO creates an [IOEither] from an [IO] instance, invoking [IO] for each invocation of [IOEither] +func FromIO[E, A any](mr I.IO[A]) IOEither[E, A] { + return G.FromIO[IOEither[E, A]](mr) +} + +// FromLazy creates an [IOEither] from a [Lazy] instance, invoking [Lazy] for each invocation of [IOEither] +func FromLazy[E, A any](mr L.Lazy[A]) IOEither[E, A] { + return G.FromIO[IOEither[E, A]](mr) +} + +func MonadMap[E, A, B any](fa IOEither[E, A], f func(A) B) IOEither[E, B] { + return G.MonadMap[IOEither[E, A], IOEither[E, B]](fa, f) +} + +func Map[E, A, B any](f func(A) B) func(IOEither[E, A]) IOEither[E, B] { + return G.Map[IOEither[E, A], IOEither[E, B]](f) +} + +func MonadMapTo[E, A, B any](fa IOEither[E, A], b B) IOEither[E, B] { + return G.MonadMapTo[IOEither[E, A], IOEither[E, B]](fa, b) +} + +func MapTo[E, A, B any](b B) func(IOEither[E, A]) IOEither[E, B] { + return G.MapTo[IOEither[E, A], IOEither[E, B]](b) +} + +func MonadChain[E, A, B any](fa IOEither[E, A], f func(A) IOEither[E, B]) IOEither[E, B] { + return G.MonadChain(fa, f) +} + +func Chain[E, A, B any](f func(A) IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] { + return G.Chain[IOEither[E, A]](f) +} + +func MonadChainEitherK[E, A, B any](ma IOEither[E, A], f func(A) ET.Either[E, B]) IOEither[E, B] { + return G.MonadChainEitherK[IOEither[E, A], IOEither[E, B]](ma, f) +} + +func ChainEitherK[E, A, B any](f func(A) ET.Either[E, B]) func(IOEither[E, A]) IOEither[E, B] { + return G.ChainEitherK[IOEither[E, A], IOEither[E, B]](f) +} + +func MonadAp[B, E, A any](mab IOEither[E, func(A) B], ma IOEither[E, A]) IOEither[E, B] { + return G.MonadAp[IOEither[E, B]](mab, ma) +} + +// Ap is an alias of [ApPar] +func Ap[B, E, A any](ma IOEither[E, A]) func(IOEither[E, func(A) B]) IOEither[E, B] { + return G.Ap[IOEither[E, B], IOEither[E, func(A) B]](ma) +} + +func MonadApPar[B, E, A any](mab IOEither[E, func(A) B], ma IOEither[E, A]) IOEither[E, B] { + return G.MonadApPar[IOEither[E, B]](mab, ma) +} + +// ApPar applies function and value in parallel +func ApPar[B, E, A any](ma IOEither[E, A]) func(IOEither[E, func(A) B]) IOEither[E, B] { + return G.ApPar[IOEither[E, B], IOEither[E, func(A) B]](ma) +} + +func MonadApSeq[B, E, A any](mab IOEither[E, func(A) B], ma IOEither[E, A]) IOEither[E, B] { + return G.MonadApSeq[IOEither[E, B]](mab, ma) +} + +// ApSeq applies function and value sequentially +func ApSeq[B, E, A any](ma IOEither[E, A]) func(IOEither[E, func(A) B]) IOEither[E, B] { + return G.ApSeq[IOEither[E, B], IOEither[E, func(A) B]](ma) +} + +func Flatten[E, A any](mma IOEither[E, IOEither[E, A]]) IOEither[E, A] { + return G.Flatten(mma) +} + +func TryCatch[E, A any](f func() (A, error), onThrow func(error) E) IOEither[E, A] { + return G.TryCatch[IOEither[E, A]](f, onThrow) +} + +func TryCatchError[A any](f func() (A, error)) IOEither[error, A] { + return G.TryCatchError[IOEither[error, A]](f) +} + +func Memoize[E, A any](ma IOEither[E, A]) IOEither[E, A] { + return G.Memoize(ma) +} + +func MonadMapLeft[E1, E2, A any](fa IOEither[E1, A], f func(E1) E2) IOEither[E2, A] { + return G.MonadMapLeft[IOEither[E1, A], IOEither[E2, A]](fa, f) +} + +func MapLeft[A, E1, E2 any](f func(E1) E2) func(IOEither[E1, A]) IOEither[E2, A] { + return G.MapLeft[IOEither[E1, A], IOEither[E2, A]](f) +} + +func MonadBiMap[E1, E2, A, B any](fa IOEither[E1, A], f func(E1) E2, g func(A) B) IOEither[E2, B] { + return G.MonadBiMap[IOEither[E1, A], IOEither[E2, B]](fa, f, g) +} + +// BiMap maps a pair of functions over the two type arguments of the bifunctor. +func BiMap[E1, E2, A, B any](f func(E1) E2, g func(A) B) func(IOEither[E1, A]) IOEither[E2, B] { + return G.BiMap[IOEither[E1, A], IOEither[E2, B]](f, g) +} + +// Fold converts an IOEither into an IO +func Fold[E, A, B any](onLeft func(E) I.IO[B], onRight func(A) I.IO[B]) func(IOEither[E, A]) I.IO[B] { + return G.Fold[IOEither[E, A]](onLeft, onRight) +} + +// GetOrElse extracts the value or maps the error +func GetOrElse[E, A any](onLeft func(E) I.IO[A]) func(IOEither[E, A]) I.IO[A] { + return G.GetOrElse[IOEither[E, A]](onLeft) +} + +// MonadChainTo composes to the second monad ignoring the return value of the first +func MonadChainTo[A, E, B any](fa IOEither[E, A], fb IOEither[E, B]) IOEither[E, B] { + return G.MonadChainTo(fa, fb) +} + +// ChainTo composes to the second [IOEither] monad ignoring the return value of the first +func ChainTo[A, E, B any](fb IOEither[E, B]) func(IOEither[E, A]) IOEither[E, B] { + return G.ChainTo[IOEither[E, A]](fb) +} + +// MonadChainFirst runs the [IOEither] monad returned by the function but returns the result of the original monad +func MonadChainFirst[E, A, B any](ma IOEither[E, A], f func(A) IOEither[E, B]) IOEither[E, A] { + return G.MonadChainFirst(ma, f) +} + +// ChainFirst runs the [IOEither] monad returned by the function but returns the result of the original monad +func ChainFirst[E, A, B any](f func(A) IOEither[E, B]) func(IOEither[E, A]) IOEither[E, A] { + return G.ChainFirst[IOEither[E, A]](f) +} + +func MonadChainFirstEitherK[A, E, B any](ma IOEither[E, A], f func(A) ET.Either[E, B]) IOEither[E, A] { + return G.MonadChainFirstEitherK[IOEither[E, A]](ma, f) +} + +func ChainFirstEitherK[A, E, B any](f func(A) ET.Either[E, B]) func(ma IOEither[E, A]) IOEither[E, A] { + return G.ChainFirstEitherK[IOEither[E, A]](f) +} + +// MonadChainFirstIOK runs [IO] the monad returned by the function but returns the result of the original monad +func MonadChainFirstIOK[E, A, B any](ma IOEither[E, A], f func(A) I.IO[B]) IOEither[E, A] { + return G.MonadChainFirstIOK(ma, f) +} + +// ChainFirstIOK runs the [IO] monad returned by the function but returns the result of the original monad +func ChainFirstIOK[E, A, B any](f func(A) I.IO[B]) func(IOEither[E, A]) IOEither[E, A] { + return G.ChainFirstIOK[IOEither[E, A]](f) +} + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[A, E, R, ANY any](onCreate IOEither[E, R], onRelease func(R) IOEither[E, ANY]) func(func(R) IOEither[E, A]) IOEither[E, A] { + return G.WithResource[IOEither[E, A]](onCreate, onRelease) +} + +// Swap changes the order of type parameters +func Swap[E, A any](val IOEither[E, A]) IOEither[A, E] { + return G.Swap[IOEither[E, A], IOEither[A, E]](val) +} + +// FromImpure converts a side effect without a return value into a side effect that returns any +func FromImpure[E any](f func()) IOEither[E, any] { + return G.FromImpure[IOEither[E, any]](f) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[E, A any](gen L.Lazy[IOEither[E, A]]) IOEither[E, A] { + return G.Defer[IOEither[E, A]](gen) +} + +// MonadAlt identifies an associative operation on a type constructor +func MonadAlt[E, A any](first IOEither[E, A], second L.Lazy[IOEither[E, A]]) IOEither[E, A] { + return G.MonadAlt(first, second) +} + +// Alt identifies an associative operation on a type constructor +func Alt[E, A any](second L.Lazy[IOEither[E, A]]) func(IOEither[E, A]) IOEither[E, A] { + return G.Alt(second) +} + +func MonadFlap[E, B, A any](fab IOEither[E, func(A) B], a A) IOEither[E, B] { + return G.MonadFlap[IOEither[E, func(A) B], IOEither[E, B]](fab, a) +} + +func Flap[E, B, A any](a A) func(IOEither[E, func(A) B]) IOEither[E, B] { + return G.Flap[IOEither[E, func(A) B], IOEither[E, B]](a) +} + +// ToIOOption converts an [IOEither] to an [IOO.IOOption] +func ToIOOption[E, A any](ioe IOEither[E, A]) IOO.IOOption[A] { + return G.ToIOOption[IOO.IOOption[A]](ioe) +} + +// Delay creates an operation that passes in the value after some delay +func Delay[E, A any](delay time.Duration) func(IOEither[E, A]) IOEither[E, A] { + return G.Delay[IOEither[E, A]](delay) +} + +// After creates an operation that passes after the given [time.Time] +func After[E, A any](timestamp time.Time) func(IOEither[E, A]) IOEither[E, A] { + return G.After[IOEither[E, A]](timestamp) +} diff --git a/v2/ioeither/ioeither_test.go b/v2/ioeither/ioeither_test.go new file mode 100644 index 0000000..fc72f60 --- /dev/null +++ b/v2/ioeither/ioeither_test.go @@ -0,0 +1,136 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "fmt" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + I "github.com/IBM/fp-go/v2/io" + IG "github.com/IBM/fp-go/v2/io/generic" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +func TestMap(t *testing.T) { + assert.Equal(t, E.Of[error](2), F.Pipe1( + Of[error](1), + Map[error](utils.Double), + )()) + +} + +func TestChainEitherK(t *testing.T) { + f := ChainEitherK(func(n int) E.Either[string, int] { + if n > 0 { + return E.Of[string](n) + } + return E.Left[int]("a") + + }) + assert.Equal(t, E.Right[string](1), f(Right[string](1))()) + assert.Equal(t, E.Left[int]("a"), f(Right[string](-1))()) + assert.Equal(t, E.Left[int]("b"), f(Left[int]("b"))()) +} + +func TestChainOptionK(t *testing.T) { + f := ChainOptionK[int, int](F.Constant("a"))(func(n int) O.Option[int] { + if n > 0 { + return O.Some(n) + } + return O.None[int]() + }) + + assert.Equal(t, E.Right[string](1), f(Right[string](1))()) + assert.Equal(t, E.Left[int]("a"), f(Right[string](-1))()) + assert.Equal(t, E.Left[int]("b"), f(Left[int]("b"))()) +} + +func TestFromOption(t *testing.T) { + f := FromOption[int](F.Constant("a")) + assert.Equal(t, E.Right[string](1), f(O.Some(1))()) + assert.Equal(t, E.Left[int]("a"), f(O.None[int]())()) +} + +func TestChainIOK(t *testing.T) { + f := ChainIOK[string](func(n int) I.IO[string] { + return I.MakeIO(func() string { + return fmt.Sprintf("%d", n) + }) + }) + + assert.Equal(t, E.Right[string]("1"), f(Right[string](1))()) + assert.Equal(t, E.Left[string, string]("b"), f(Left[int]("b"))()) +} + +func TestChainWithIO(t *testing.T) { + + r := F.Pipe1( + Of[error]("test"), + // sad, we need the generics version ... + IG.Map[IOEither[error, string], I.IO[bool]](E.IsRight[error, string]), + ) + + assert.True(t, r()) +} + +func TestChainFirst(t *testing.T) { + f := func(a string) IOEither[string, int] { + if len(a) > 2 { + return Of[string](len(a)) + } + return Left[int]("foo") + } + good := Of[string]("foo") + bad := Of[string]("a") + ch := ChainFirst(f) + + assert.Equal(t, E.Of[string]("foo"), F.Pipe1(good, ch)()) + assert.Equal(t, E.Left[string, string]("foo"), F.Pipe1(bad, ch)()) +} + +func TestChainFirstIOK(t *testing.T) { + f := func(a string) I.IO[int] { + return I.Of(len(a)) + } + good := Of[string]("foo") + ch := ChainFirstIOK[string](f) + + assert.Equal(t, E.Of[string]("foo"), F.Pipe1(good, ch)()) +} + +func TestApFirst(t *testing.T) { + + x := F.Pipe1( + Of[error]("a"), + ApFirst[string](Of[error]("b")), + ) + + assert.Equal(t, E.Of[error]("a"), x()) +} + +func TestApSecond(t *testing.T) { + + x := F.Pipe1( + Of[error]("a"), + ApSecond[string](Of[error]("b")), + ) + + assert.Equal(t, E.Of[error]("b"), x()) +} diff --git a/v2/ioeither/logging.go b/v2/ioeither/logging.go new file mode 100644 index 0000000..4846646 --- /dev/null +++ b/v2/ioeither/logging.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// LogJson converts the argument to pretty printed JSON and then logs it via the format string +// Can be used with [ChainFirst] +// +// Deprecated: use [LogJSON] instead +func LogJson[A any](prefix string) func(A) IOEither[error, any] { + return G.LogJson[IOEither[error, any], A](prefix) +} + +// LogJSON converts the argument to pretty printed JSON and then logs it via the format string +// Can be used with [ChainFirst] +func LogJSON[A any](prefix string) func(A) IOEither[error, any] { + return G.LogJSON[IOEither[error, any], A](prefix) +} diff --git a/v2/ioeither/logging_test.go b/v2/ioeither/logging_test.go new file mode 100644 index 0000000..f4011a2 --- /dev/null +++ b/v2/ioeither/logging_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestLogging(t *testing.T) { + + type SomeData struct { + Key string `json:"key"` + Value string `json:"value"` + } + + src := &SomeData{Key: "key", Value: "value"} + + res := F.Pipe1( + Of[error](src), + ChainFirst(LogJSON[*SomeData]("Data: \n%s")), + ) + + dst := res() + assert.Equal(t, E.Of[error](src), dst) +} diff --git a/v2/ioeither/monad.go b/v2/ioeither/monad.go new file mode 100644 index 0000000..03838d1 --- /dev/null +++ b/v2/ioeither/monad.go @@ -0,0 +1,38 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// Pointed returns the pointed operations for [IOEither] +func Pointed[E, A any]() pointed.Pointed[A, IOEither[E, A]] { + return G.Pointed[E, A, IOEither[E, A]]() +} + +// Functor returns the functor operations for [IOEither] +func Functor[E, A, B any]() functor.Functor[A, B, IOEither[E, A], IOEither[E, B]] { + return G.Functor[E, A, B, IOEither[E, A], IOEither[E, B]]() +} + +// Monad returns the monadic operations for [IOEither] +func Monad[E, A, B any]() monad.Monad[A, B, IOEither[E, A], IOEither[E, B], IOEither[E, func(A) B]] { + return G.Monad[E, A, B, IOEither[E, A], IOEither[E, B], IOEither[E, func(A) B]]() +} diff --git a/v2/ioeither/monoid.go b/v2/ioeither/monoid.go new file mode 100644 index 0000000..8c3db3f --- /dev/null +++ b/v2/ioeither/monoid.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + G "github.com/IBM/fp-go/v2/ioeither/generic" + M "github.com/IBM/fp-go/v2/monoid" +) + +// ApplicativeMonoid returns a [Monoid] that concatenates [IOEither] instances via their applicative +func ApplicativeMonoid[E, A any]( + m M.Monoid[A], +) M.Monoid[IOEither[E, A]] { + return G.ApplicativeMonoid[IOEither[E, A], IOEither[E, func(A) A]](m) +} + +// ApplicativeMonoid returns a [Monoid] that concatenates [IOEither] instances via their applicative +func ApplicativeMonoidSeq[E, A any]( + m M.Monoid[A], +) M.Monoid[IOEither[E, A]] { + return G.ApplicativeMonoidSeq[IOEither[E, A], IOEither[E, func(A) A]](m) +} + +// ApplicativeMonoid returns a [Monoid] that concatenates [IOEither] instances via their applicative +func ApplicativeMonoidPar[E, A any]( + m M.Monoid[A], +) M.Monoid[IOEither[E, A]] { + return G.ApplicativeMonoid[IOEither[E, A], IOEither[E, func(A) A]](m) +} diff --git a/v2/ioeither/monoid_test.go b/v2/ioeither/monoid_test.go new file mode 100644 index 0000000..3c74974 --- /dev/null +++ b/v2/ioeither/monoid_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "fmt" + "testing" + + E "github.com/IBM/fp-go/v2/either" + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +func TestApplicativeMonoid(t *testing.T) { + m := ApplicativeMonoid[error](S.Monoid) + + // good cases + assert.Equal(t, E.Of[error]("ab"), m.Concat(Of[error]("a"), Of[error]("b"))()) + assert.Equal(t, E.Of[error]("a"), m.Concat(Of[error]("a"), m.Empty())()) + assert.Equal(t, E.Of[error]("b"), m.Concat(m.Empty(), Of[error]("b"))()) + + // bad cases + e1 := fmt.Errorf("e1") + e2 := fmt.Errorf("e1") + + assert.Equal(t, E.Left[string](e1), m.Concat(Left[string](e1), Of[error]("b"))()) + assert.Equal(t, E.Left[string](e1), m.Concat(Left[string](e1), Left[string](e2))()) + assert.Equal(t, E.Left[string](e2), m.Concat(Of[error]("a"), Left[string](e2))()) +} diff --git a/v2/ioeither/retry.go b/v2/ioeither/retry.go new file mode 100644 index 0000000..290a822 --- /dev/null +++ b/v2/ioeither/retry.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/ioeither/generic" + R "github.com/IBM/fp-go/v2/retry" +) + +// Retrying will retry the actions according to the check policy +// +// policy - refers to the retry policy +// action - converts a status into an operation to be executed +// check - checks if the result of the action needs to be retried +func Retrying[E, A any]( + policy R.RetryPolicy, + action func(R.RetryStatus) IOEither[E, A], + check func(ET.Either[E, A]) bool, +) IOEither[E, A] { + return G.Retrying(policy, action, check) +} diff --git a/v2/ioeither/retry_test.go b/v2/ioeither/retry_test.go new file mode 100644 index 0000000..cb8e6d9 --- /dev/null +++ b/v2/ioeither/retry_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "fmt" + "testing" + "time" + + E "github.com/IBM/fp-go/v2/either" + R "github.com/IBM/fp-go/v2/retry" + "github.com/stretchr/testify/assert" +) + +var expLogBackoff = R.ExponentialBackoff(10 * time.Millisecond) + +// our retry policy with a 1s cap +var testLogPolicy = R.CapDelay( + 2*time.Second, + R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)), +) + +func TestRetry(t *testing.T) { + action := func(status R.RetryStatus) IOEither[error, string] { + if status.IterNumber < 5 { + return Left[string](fmt.Errorf("retrying %d", status.IterNumber)) + } + return Of[error](fmt.Sprintf("Retrying %d", status.IterNumber)) + } + check := E.IsLeft[error, string] + + r := Retrying(testLogPolicy, action, check) + + assert.Equal(t, E.Of[error]("Retrying 5"), r()) +} diff --git a/v2/ioeither/semigroup.go b/v2/ioeither/semigroup.go new file mode 100644 index 0000000..e3a5e95 --- /dev/null +++ b/v2/ioeither/semigroup.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + G "github.com/IBM/fp-go/v2/ioeither/generic" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative +func AltSemigroup[E, A any]() S.Semigroup[IOEither[E, A]] { + return G.AltSemigroup[IOEither[E, A]]() +} diff --git a/v2/ioeither/sequence_test.go b/v2/ioeither/sequence_test.go new file mode 100644 index 0000000..a99c7e7 --- /dev/null +++ b/v2/ioeither/sequence_test.go @@ -0,0 +1,82 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "fmt" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" + + TST "github.com/IBM/fp-go/v2/internal/testing" + + "testing" +) + +func TestMapSeq(t *testing.T) { + var results []string + + handler := func(value string) IOEither[error, string] { + return func() E.Either[error, string] { + results = append(results, value) + return E.Of[error](value) + } + } + + src := A.From("a", "b", "c") + + res := F.Pipe2( + src, + TraverseArraySeq(handler), + Map[error](func(data []string) bool { + return assert.Equal(t, data, results) + }), + ) + + assert.Equal(t, E.Of[error](true), res()) +} + +func TestSequenceArray(t *testing.T) { + + s := TST.SequenceArrayTest( + FromStrictEquals[error, bool](), + Pointed[error, string](), + Pointed[error, bool](), + Functor[error, []string, bool](), + SequenceArray[error, string], + ) + + for i := 0; i < 10; i++ { + t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i)) + } +} + +func TestSequenceArrayError(t *testing.T) { + + s := TST.SequenceArrayErrorTest( + FromStrictEquals[error, bool](), + Left[string, error], + Left[bool, error], + Pointed[error, string](), + Pointed[error, bool](), + Functor[error, []string, bool](), + SequenceArray[error, string], + ) + // run across four bits + s(4)(t) +} diff --git a/v2/ioeither/sync.go b/v2/ioeither/sync.go new file mode 100644 index 0000000..b2e65e1 --- /dev/null +++ b/v2/ioeither/sync.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "context" + + IO "github.com/IBM/fp-go/v2/io" + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[E, A any](lock IO.IO[context.CancelFunc]) func(fa IOEither[E, A]) IOEither[E, A] { + return G.WithLock[IOEither[E, A]](lock) +} diff --git a/v2/ioeither/testing/laws.go b/v2/ioeither/testing/laws.go new file mode 100644 index 0000000..b0765c9 --- /dev/null +++ b/v2/ioeither/testing/laws.go @@ -0,0 +1,76 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" + IOE "github.com/IBM/fp-go/v2/ioeither" +) + +// AssertLaws asserts the apply monad laws for the `IOEither` monad +func AssertLaws[E, A, B, C any](t *testing.T, + eqe EQ.Eq[E], + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + return L.AssertLaws(t, + IOE.Eq(ET.Eq(eqe, eqa)), + IOE.Eq(ET.Eq(eqe, eqb)), + IOE.Eq(ET.Eq(eqe, eqc)), + + IOE.Of[E, A], + IOE.Of[E, B], + IOE.Of[E, C], + + IOE.Of[E, func(A) A], + IOE.Of[E, func(A) B], + IOE.Of[E, func(B) C], + IOE.Of[E, func(func(A) B) B], + + IOE.MonadMap[E, A, A], + IOE.MonadMap[E, A, B], + IOE.MonadMap[E, A, C], + IOE.MonadMap[E, B, C], + + IOE.MonadMap[E, func(B) C, func(func(A) B) func(A) C], + + IOE.MonadChain[E, A, A], + IOE.MonadChain[E, A, B], + IOE.MonadChain[E, A, C], + IOE.MonadChain[E, B, C], + + IOE.MonadAp[A, E, A], + IOE.MonadAp[B, E, A], + IOE.MonadAp[C, E, B], + IOE.MonadAp[C, E, A], + + IOE.MonadAp[B, E, func(A) B], + IOE.MonadAp[func(A) C, E, func(A) B], + + ab, + bc, + ) + +} diff --git a/v2/ioeither/testing/laws_test.go b/v2/ioeither/testing/laws_test.go new file mode 100644 index 0000000..8d2ec6a --- /dev/null +++ b/v2/ioeither/testing/laws_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqe := EQ.FromStrictEquals[string]() + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqe, eqa, eqb, eqc, ab, bc) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/ioeither/traverse.go b/v2/ioeither/traverse.go new file mode 100644 index 0000000..556697d --- /dev/null +++ b/v2/ioeither/traverse.go @@ -0,0 +1,110 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + G "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// TraverseArray transforms an array +func TraverseArray[E, A, B any](f func(A) IOEither[E, B]) func([]A) IOEither[E, []B] { + return G.TraverseArray[IOEither[E, B], IOEither[E, []B], []A](f) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[E, A, B any](f func(int, A) IOEither[E, B]) func([]A) IOEither[E, []B] { + return G.TraverseArrayWithIndex[IOEither[E, B], IOEither[E, []B], []A](f) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[E, A any](ma []IOEither[E, A]) IOEither[E, []A] { + return G.SequenceArray[IOEither[E, A], IOEither[E, []A]](ma) +} + +// TraverseRecord transforms a record +func TraverseRecord[K comparable, E, A, B any](f func(A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] { + return G.TraverseRecord[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f) +} + +// TraverseRecordWithIndex transforms a record +func TraverseRecordWithIndex[K comparable, E, A, B any](f func(K, A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] { + return G.TraverseRecordWithIndex[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f) +} + +// SequenceRecord converts a homogeneous sequence of either into an either of sequence +func SequenceRecord[K comparable, E, A any](ma map[K]IOEither[E, A]) IOEither[E, map[K]A] { + return G.SequenceRecord[IOEither[E, A], IOEither[E, map[K]A]](ma) +} + +// TraverseArraySeq transforms an array +func TraverseArraySeq[E, A, B any](f func(A) IOEither[E, B]) func([]A) IOEither[E, []B] { + return G.TraverseArraySeq[IOEither[E, B], IOEither[E, []B], []A](f) +} + +// TraverseArrayWithIndexSeq transforms an array +func TraverseArrayWithIndexSeq[E, A, B any](f func(int, A) IOEither[E, B]) func([]A) IOEither[E, []B] { + return G.TraverseArrayWithIndexSeq[IOEither[E, B], IOEither[E, []B], []A](f) +} + +// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence +func SequenceArraySeq[E, A any](ma []IOEither[E, A]) IOEither[E, []A] { + return G.SequenceArraySeq[IOEither[E, A], IOEither[E, []A]](ma) +} + +// TraverseRecordSeq transforms a record +func TraverseRecordSeq[K comparable, E, A, B any](f func(A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] { + return G.TraverseRecordSeq[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f) +} + +// TraverseRecordWithIndexSeq transforms a record +func TraverseRecordWithIndexSeq[K comparable, E, A, B any](f func(K, A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] { + return G.TraverseRecordWithIndexSeq[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f) +} + +// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence +func SequenceRecordSeq[K comparable, E, A any](ma map[K]IOEither[E, A]) IOEither[E, map[K]A] { + return G.SequenceRecordSeq[IOEither[E, A], IOEither[E, map[K]A]](ma) +} + +// TraverseArrayPar transforms an array +func TraverseArrayPar[E, A, B any](f func(A) IOEither[E, B]) func([]A) IOEither[E, []B] { + return G.TraverseArrayPar[IOEither[E, B], IOEither[E, []B], []A](f) +} + +// TraverseArrayWithIndexPar transforms an array +func TraverseArrayWithIndexPar[E, A, B any](f func(int, A) IOEither[E, B]) func([]A) IOEither[E, []B] { + return G.TraverseArrayWithIndexPar[IOEither[E, B], IOEither[E, []B], []A](f) +} + +// SequenceArrayPar converts a homogeneous Paruence of either into an either of Paruence +func SequenceArrayPar[E, A any](ma []IOEither[E, A]) IOEither[E, []A] { + return G.SequenceArrayPar[IOEither[E, A], IOEither[E, []A]](ma) +} + +// TraverseRecordPar transforms a record +func TraverseRecordPar[K comparable, E, A, B any](f func(A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] { + return G.TraverseRecordPar[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f) +} + +// TraverseRecordWithIndexPar transforms a record +func TraverseRecordWithIndexPar[K comparable, E, A, B any](f func(K, A) IOEither[E, B]) func(map[K]A) IOEither[E, map[K]B] { + return G.TraverseRecordWithIndexPar[IOEither[E, B], IOEither[E, map[K]B], map[K]A](f) +} + +// SequenceRecordPar converts a homogeneous Paruence of either into an either of Paruence +func SequenceRecordPar[K comparable, E, A any](ma map[K]IOEither[E, A]) IOEither[E, map[K]A] { + return G.SequenceRecordPar[IOEither[E, A], IOEither[E, map[K]A]](ma) +} diff --git a/v2/ioeither/traverse_test.go b/v2/ioeither/traverse_test.go new file mode 100644 index 0000000..fd16bab --- /dev/null +++ b/v2/ioeither/traverse_test.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioeither + +import ( + "fmt" + "testing" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + "github.com/stretchr/testify/assert" +) + +func TestTraverseArray(t *testing.T) { + + src := A.From("A", "B") + + trfrm := TraverseArrayWithIndex(func(idx int, data string) IOEither[error, string] { + return Of[error](fmt.Sprintf("idx: %d, data: %s", idx, data)) + }) + + assert.Equal(t, E.Of[error](A.From("idx: 0, data: A", "idx: 1, data: B")), trfrm(src)()) + +} diff --git a/v2/iooption/array.go b/v2/iooption/array.go new file mode 100644 index 0000000..38d718f --- /dev/null +++ b/v2/iooption/array.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + G "github.com/IBM/fp-go/v2/iooption/generic" +) + +// TraverseArray transforms an array +func TraverseArray[A, B any](f func(A) IOOption[B]) func([]A) IOOption[[]B] { + return G.TraverseArray[IOOption[B], IOOption[[]B], []A](f) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[A, B any](f func(int, A) IOOption[B]) func([]A) IOOption[[]B] { + return G.TraverseArrayWithIndex[IOOption[B], IOOption[[]B], []A](f) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[A any](ma []IOOption[A]) IOOption[[]A] { + return G.SequenceArray[IOOption[A], IOOption[[]A], []IOOption[A], []A, A](ma) +} diff --git a/v2/iooption/bind.go b/v2/iooption/bind.go new file mode 100644 index 0000000..d7aeb9d --- /dev/null +++ b/v2/iooption/bind.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + G "github.com/IBM/fp-go/v2/iooption/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) IOOption[S] { + return G.Do[IOOption[S], S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) IOOption[T], +) func(IOOption[S1]) IOOption[S2] { + return G.Bind[IOOption[S1], IOOption[S2], IOOption[T], S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(IOOption[S1]) IOOption[S2] { + return G.Let[IOOption[S1], IOOption[S2], S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(IOOption[S1]) IOOption[S2] { + return G.LetTo[IOOption[S1], IOOption[S2], S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func(IOOption[T]) IOOption[S1] { + return G.BindTo[IOOption[S1], IOOption[T], S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa IOOption[T], +) func(IOOption[S1]) IOOption[S2] { + return G.ApS[IOOption[S1], IOOption[S2], IOOption[T], S1, S2, T](setter, fa) +} diff --git a/v2/iooption/bind_test.go b/v2/iooption/bind_test.go new file mode 100644 index 0000000..ded797c --- /dev/null +++ b/v2/iooption/bind_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) IOOption[string] { + return Of("Doe") +} + +func getGivenName(s utils.WithLastName) IOOption[string] { + return Of("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(), O.Of("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + ApS(utils.SetLastName, Of("Doe")), + ApS(utils.SetGivenName, Of("John")), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(), O.Of("John Doe")) +} diff --git a/v2/iooption/bracket.go b/v2/iooption/bracket.go new file mode 100644 index 0000000..a6cd447 --- /dev/null +++ b/v2/iooption/bracket.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + G "github.com/IBM/fp-go/v2/iooption/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[E, A, B, ANY any]( + acquire IOOption[A], + use func(A) IOOption[B], + release func(A, O.Option[B]) IOOption[ANY], +) IOOption[B] { + return G.Bracket(acquire, use, release) +} diff --git a/v2/iooption/doc.go b/v2/iooption/doc.go new file mode 100644 index 0000000..6bfcf1b --- /dev/null +++ b/v2/iooption/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +//go:generate go run .. iooption --count 10 --filename gen.go diff --git a/v2/iooption/eq.go b/v2/iooption/eq.go new file mode 100644 index 0000000..8cc1ed5 --- /dev/null +++ b/v2/iooption/eq.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/iooption/generic" +) + +// Eq implements the equals predicate for values contained in the IO monad +func Eq[A any](e EQ.Eq[A]) EQ.Eq[IOOption[A]] { + return G.Eq[IOOption[A]](e) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[A comparable]() EQ.Eq[IOOption[A]] { + return G.FromStrictEquals[IOOption[A]]() +} diff --git a/v2/iooption/gen.go b/v2/iooption/gen.go new file mode 100644 index 0000000..769ed6a --- /dev/null +++ b/v2/iooption/gen.go @@ -0,0 +1,375 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:16.7939127 +0100 CET m=+0.053853301 + +package iooption + +import ( + G "github.com/IBM/fp-go/v2/iooption/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT1 converts 1 [IOOption[T]] into a [IOOption[T.Tuple1[T1]]] +func SequenceT1[T1 any]( + t1 IOOption[T1], +) IOOption[T.Tuple1[T1]] { + return G.SequenceT1[ + IOOption[T.Tuple1[T1]], + IOOption[T1], + ](t1) +} + +// SequenceTuple1 converts a [T.Tuple1[IOOption[T]]] into a [IOOption[T.Tuple1[T1]]] +func SequenceTuple1[T1 any](t T.Tuple1[IOOption[T1]]) IOOption[T.Tuple1[T1]] { + return G.SequenceTuple1[ + IOOption[T.Tuple1[T1]], + IOOption[T1], + ](t) +} + +// TraverseTuple1 converts a [T.Tuple1[IOOption[T]]] into a [IOOption[T.Tuple1[T1]]] +func TraverseTuple1[F1 ~func(A1) IOOption[T1], A1, T1 any](f1 F1) func(T.Tuple1[A1]) IOOption[T.Tuple1[T1]] { + return G.TraverseTuple1[IOOption[T.Tuple1[T1]]](f1) +} + +// SequenceT2 converts 2 [IOOption[T]] into a [IOOption[T.Tuple2[T1, T2]]] +func SequenceT2[T1, T2 any]( + t1 IOOption[T1], + t2 IOOption[T2], +) IOOption[T.Tuple2[T1, T2]] { + return G.SequenceT2[ + IOOption[T.Tuple2[T1, T2]], + IOOption[T1], + IOOption[T2], + ](t1, t2) +} + +// SequenceTuple2 converts a [T.Tuple2[IOOption[T]]] into a [IOOption[T.Tuple2[T1, T2]]] +func SequenceTuple2[T1, T2 any](t T.Tuple2[IOOption[T1], IOOption[T2]]) IOOption[T.Tuple2[T1, T2]] { + return G.SequenceTuple2[ + IOOption[T.Tuple2[T1, T2]], + IOOption[T1], + IOOption[T2], + ](t) +} + +// TraverseTuple2 converts a [T.Tuple2[IOOption[T]]] into a [IOOption[T.Tuple2[T1, T2]]] +func TraverseTuple2[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], A1, A2, T1, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) IOOption[T.Tuple2[T1, T2]] { + return G.TraverseTuple2[IOOption[T.Tuple2[T1, T2]]](f1, f2) +} + +// SequenceT3 converts 3 [IOOption[T]] into a [IOOption[T.Tuple3[T1, T2, T3]]] +func SequenceT3[T1, T2, T3 any]( + t1 IOOption[T1], + t2 IOOption[T2], + t3 IOOption[T3], +) IOOption[T.Tuple3[T1, T2, T3]] { + return G.SequenceT3[ + IOOption[T.Tuple3[T1, T2, T3]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + ](t1, t2, t3) +} + +// SequenceTuple3 converts a [T.Tuple3[IOOption[T]]] into a [IOOption[T.Tuple3[T1, T2, T3]]] +func SequenceTuple3[T1, T2, T3 any](t T.Tuple3[IOOption[T1], IOOption[T2], IOOption[T3]]) IOOption[T.Tuple3[T1, T2, T3]] { + return G.SequenceTuple3[ + IOOption[T.Tuple3[T1, T2, T3]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + ](t) +} + +// TraverseTuple3 converts a [T.Tuple3[IOOption[T]]] into a [IOOption[T.Tuple3[T1, T2, T3]]] +func TraverseTuple3[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], F3 ~func(A3) IOOption[T3], A1, A2, A3, T1, T2, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) IOOption[T.Tuple3[T1, T2, T3]] { + return G.TraverseTuple3[IOOption[T.Tuple3[T1, T2, T3]]](f1, f2, f3) +} + +// SequenceT4 converts 4 [IOOption[T]] into a [IOOption[T.Tuple4[T1, T2, T3, T4]]] +func SequenceT4[T1, T2, T3, T4 any]( + t1 IOOption[T1], + t2 IOOption[T2], + t3 IOOption[T3], + t4 IOOption[T4], +) IOOption[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceT4[ + IOOption[T.Tuple4[T1, T2, T3, T4]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + ](t1, t2, t3, t4) +} + +// SequenceTuple4 converts a [T.Tuple4[IOOption[T]]] into a [IOOption[T.Tuple4[T1, T2, T3, T4]]] +func SequenceTuple4[T1, T2, T3, T4 any](t T.Tuple4[IOOption[T1], IOOption[T2], IOOption[T3], IOOption[T4]]) IOOption[T.Tuple4[T1, T2, T3, T4]] { + return G.SequenceTuple4[ + IOOption[T.Tuple4[T1, T2, T3, T4]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + ](t) +} + +// TraverseTuple4 converts a [T.Tuple4[IOOption[T]]] into a [IOOption[T.Tuple4[T1, T2, T3, T4]]] +func TraverseTuple4[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], F3 ~func(A3) IOOption[T3], F4 ~func(A4) IOOption[T4], A1, A2, A3, A4, T1, T2, T3, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) IOOption[T.Tuple4[T1, T2, T3, T4]] { + return G.TraverseTuple4[IOOption[T.Tuple4[T1, T2, T3, T4]]](f1, f2, f3, f4) +} + +// SequenceT5 converts 5 [IOOption[T]] into a [IOOption[T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceT5[T1, T2, T3, T4, T5 any]( + t1 IOOption[T1], + t2 IOOption[T2], + t3 IOOption[T3], + t4 IOOption[T4], + t5 IOOption[T5], +) IOOption[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceT5[ + IOOption[T.Tuple5[T1, T2, T3, T4, T5]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + ](t1, t2, t3, t4, t5) +} + +// SequenceTuple5 converts a [T.Tuple5[IOOption[T]]] into a [IOOption[T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceTuple5[T1, T2, T3, T4, T5 any](t T.Tuple5[IOOption[T1], IOOption[T2], IOOption[T3], IOOption[T4], IOOption[T5]]) IOOption[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.SequenceTuple5[ + IOOption[T.Tuple5[T1, T2, T3, T4, T5]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + ](t) +} + +// TraverseTuple5 converts a [T.Tuple5[IOOption[T]]] into a [IOOption[T.Tuple5[T1, T2, T3, T4, T5]]] +func TraverseTuple5[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], F3 ~func(A3) IOOption[T3], F4 ~func(A4) IOOption[T4], F5 ~func(A5) IOOption[T5], A1, A2, A3, A4, A5, T1, T2, T3, T4, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) IOOption[T.Tuple5[T1, T2, T3, T4, T5]] { + return G.TraverseTuple5[IOOption[T.Tuple5[T1, T2, T3, T4, T5]]](f1, f2, f3, f4, f5) +} + +// SequenceT6 converts 6 [IOOption[T]] into a [IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceT6[T1, T2, T3, T4, T5, T6 any]( + t1 IOOption[T1], + t2 IOOption[T2], + t3 IOOption[T3], + t4 IOOption[T4], + t5 IOOption[T5], + t6 IOOption[T6], +) IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceT6[ + IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + ](t1, t2, t3, t4, t5, t6) +} + +// SequenceTuple6 converts a [T.Tuple6[IOOption[T]]] into a [IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceTuple6[T1, T2, T3, T4, T5, T6 any](t T.Tuple6[IOOption[T1], IOOption[T2], IOOption[T3], IOOption[T4], IOOption[T5], IOOption[T6]]) IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.SequenceTuple6[ + IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + ](t) +} + +// TraverseTuple6 converts a [T.Tuple6[IOOption[T]]] into a [IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func TraverseTuple6[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], F3 ~func(A3) IOOption[T3], F4 ~func(A4) IOOption[T4], F5 ~func(A5) IOOption[T5], F6 ~func(A6) IOOption[T6], A1, A2, A3, A4, A5, A6, T1, T2, T3, T4, T5, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return G.TraverseTuple6[IOOption[T.Tuple6[T1, T2, T3, T4, T5, T6]]](f1, f2, f3, f4, f5, f6) +} + +// SequenceT7 converts 7 [IOOption[T]] into a [IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceT7[T1, T2, T3, T4, T5, T6, T7 any]( + t1 IOOption[T1], + t2 IOOption[T2], + t3 IOOption[T3], + t4 IOOption[T4], + t5 IOOption[T5], + t6 IOOption[T6], + t7 IOOption[T7], +) IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceT7[ + IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + IOOption[T7], + ](t1, t2, t3, t4, t5, t6, t7) +} + +// SequenceTuple7 converts a [T.Tuple7[IOOption[T]]] into a [IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceTuple7[T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[IOOption[T1], IOOption[T2], IOOption[T3], IOOption[T4], IOOption[T5], IOOption[T6], IOOption[T7]]) IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.SequenceTuple7[ + IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + IOOption[T7], + ](t) +} + +// TraverseTuple7 converts a [T.Tuple7[IOOption[T]]] into a [IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func TraverseTuple7[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], F3 ~func(A3) IOOption[T3], F4 ~func(A4) IOOption[T4], F5 ~func(A5) IOOption[T5], F6 ~func(A6) IOOption[T6], F7 ~func(A7) IOOption[T7], A1, A2, A3, A4, A5, A6, A7, T1, T2, T3, T4, T5, T6, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return G.TraverseTuple7[IOOption[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]](f1, f2, f3, f4, f5, f6, f7) +} + +// SequenceT8 converts 8 [IOOption[T]] into a [IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceT8[T1, T2, T3, T4, T5, T6, T7, T8 any]( + t1 IOOption[T1], + t2 IOOption[T2], + t3 IOOption[T3], + t4 IOOption[T4], + t5 IOOption[T5], + t6 IOOption[T6], + t7 IOOption[T7], + t8 IOOption[T8], +) IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceT8[ + IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + IOOption[T7], + IOOption[T8], + ](t1, t2, t3, t4, t5, t6, t7, t8) +} + +// SequenceTuple8 converts a [T.Tuple8[IOOption[T]]] into a [IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[IOOption[T1], IOOption[T2], IOOption[T3], IOOption[T4], IOOption[T5], IOOption[T6], IOOption[T7], IOOption[T8]]) IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.SequenceTuple8[ + IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + IOOption[T7], + IOOption[T8], + ](t) +} + +// TraverseTuple8 converts a [T.Tuple8[IOOption[T]]] into a [IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func TraverseTuple8[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], F3 ~func(A3) IOOption[T3], F4 ~func(A4) IOOption[T4], F5 ~func(A5) IOOption[T5], F6 ~func(A6) IOOption[T6], F7 ~func(A7) IOOption[T7], F8 ~func(A8) IOOption[T8], A1, A2, A3, A4, A5, A6, A7, A8, T1, T2, T3, T4, T5, T6, T7, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return G.TraverseTuple8[IOOption[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]](f1, f2, f3, f4, f5, f6, f7, f8) +} + +// SequenceT9 converts 9 [IOOption[T]] into a [IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any]( + t1 IOOption[T1], + t2 IOOption[T2], + t3 IOOption[T3], + t4 IOOption[T4], + t5 IOOption[T5], + t6 IOOption[T6], + t7 IOOption[T7], + t8 IOOption[T8], + t9 IOOption[T9], +) IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceT9[ + IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + IOOption[T7], + IOOption[T8], + IOOption[T9], + ](t1, t2, t3, t4, t5, t6, t7, t8, t9) +} + +// SequenceTuple9 converts a [T.Tuple9[IOOption[T]]] into a [IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[IOOption[T1], IOOption[T2], IOOption[T3], IOOption[T4], IOOption[T5], IOOption[T6], IOOption[T7], IOOption[T8], IOOption[T9]]) IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.SequenceTuple9[ + IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + IOOption[T7], + IOOption[T8], + IOOption[T9], + ](t) +} + +// TraverseTuple9 converts a [T.Tuple9[IOOption[T]]] into a [IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func TraverseTuple9[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], F3 ~func(A3) IOOption[T3], F4 ~func(A4) IOOption[T4], F5 ~func(A5) IOOption[T5], F6 ~func(A6) IOOption[T6], F7 ~func(A7) IOOption[T7], F8 ~func(A8) IOOption[T8], F9 ~func(A9) IOOption[T9], A1, A2, A3, A4, A5, A6, A7, A8, A9, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return G.TraverseTuple9[IOOption[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]](f1, f2, f3, f4, f5, f6, f7, f8, f9) +} + +// SequenceT10 converts 10 [IOOption[T]] into a [IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any]( + t1 IOOption[T1], + t2 IOOption[T2], + t3 IOOption[T3], + t4 IOOption[T4], + t5 IOOption[T5], + t6 IOOption[T6], + t7 IOOption[T7], + t8 IOOption[T8], + t9 IOOption[T9], + t10 IOOption[T10], +) IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceT10[ + IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + IOOption[T7], + IOOption[T8], + IOOption[T9], + IOOption[T10], + ](t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) +} + +// SequenceTuple10 converts a [T.Tuple10[IOOption[T]]] into a [IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[IOOption[T1], IOOption[T2], IOOption[T3], IOOption[T4], IOOption[T5], IOOption[T6], IOOption[T7], IOOption[T8], IOOption[T9], IOOption[T10]]) IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.SequenceTuple10[ + IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + IOOption[T1], + IOOption[T2], + IOOption[T3], + IOOption[T4], + IOOption[T5], + IOOption[T6], + IOOption[T7], + IOOption[T8], + IOOption[T9], + IOOption[T10], + ](t) +} + +// TraverseTuple10 converts a [T.Tuple10[IOOption[T]]] into a [IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func TraverseTuple10[F1 ~func(A1) IOOption[T1], F2 ~func(A2) IOOption[T2], F3 ~func(A3) IOOption[T3], F4 ~func(A4) IOOption[T4], F5 ~func(A5) IOOption[T5], F6 ~func(A6) IOOption[T6], F7 ~func(A7) IOOption[T7], F8 ~func(A8) IOOption[T8], F9 ~func(A9) IOOption[T9], F10 ~func(A10) IOOption[T10], A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return G.TraverseTuple10[IOOption[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]](f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) +} diff --git a/v2/iooption/generic/bind.go b/v2/iooption/generic/bind.go new file mode 100644 index 0000000..74b417f --- /dev/null +++ b/v2/iooption/generic/bind.go @@ -0,0 +1,90 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" + O "github.com/IBM/fp-go/v2/option" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GS ~func() O.Option[S], S any]( + empty S, +) GS { + return Of[GS](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], GT ~func() O.Option[T], S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GT, +) func(GS1) GS2 { + return C.Bind( + Chain[GS1, GS2, S1, S2], + Map[GT, GS2, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GS1) GS2 { + return F.Let( + Map[GS1, GS2, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GS1) GS2 { + return F.LetTo( + Map[GS1, GS2, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GS1 ~func() O.Option[S1], GT ~func() O.Option[T], S1, T any]( + setter func(T) S1, +) func(GT) GS1 { + return C.BindTo( + Map[GT, GS1, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GS1 ~func() O.Option[S1], GS2 ~func() O.Option[S2], GT ~func() O.Option[T], S1, S2, T any]( + setter func(T) func(S1) S2, + fa GT, +) func(GS1) GS2 { + return A.ApS( + Ap[GS2, func() O.Option[func(T) S2], GT, T, S2], + Map[GS1, func() O.Option[func(T) S2], S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/iooption/generic/bracket.go b/v2/iooption/generic/bracket.go new file mode 100644 index 0000000..8f1e7e7 --- /dev/null +++ b/v2/iooption/generic/bracket.go @@ -0,0 +1,46 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + G "github.com/IBM/fp-go/v2/internal/bracket" + I "github.com/IBM/fp-go/v2/io/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[ + GA ~func() O.Option[A], + GB ~func() O.Option[B], + GANY ~func() O.Option[ANY], + A, B, ANY any]( + + acquire GA, + use func(A) GB, + release func(A, O.Option[B]) GANY, +) GB { + return G.Bracket[GA, GB, GANY, O.Option[B], A, B]( + I.Of[GB, O.Option[B]], + MonadChain[GA, GB, A, B], + I.MonadChain[GB, GB, O.Option[B], O.Option[B]], + MonadChain[GANY, GB, ANY, B], + + acquire, + use, + release, + ) +} diff --git a/v2/iooption/generic/eq.go b/v2/iooption/generic/eq.go new file mode 100644 index 0000000..e1f7385 --- /dev/null +++ b/v2/iooption/generic/eq.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/io/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// Eq implements the equals predicate for values contained in the IO monad +func Eq[GA ~func() O.Option[A], A any](e EQ.Eq[A]) EQ.Eq[GA] { + return G.Eq[GA](O.Eq(e)) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[GA ~func() O.Option[A], A comparable]() EQ.Eq[GA] { + return Eq[GA](EQ.FromStrictEquals[A]()) +} diff --git a/v2/iooption/generic/gen.go b/v2/iooption/generic/gen.go new file mode 100644 index 0000000..65c0ffc --- /dev/null +++ b/v2/iooption/generic/gen.go @@ -0,0 +1,830 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:16.8032769 +0100 CET m=+0.063217501 +package generic + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + O "github.com/IBM/fp-go/v2/option" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT1 converts 1 [func() O.Option[T]] into a [func() O.Option[T.Tuple1[T1]]] +func SequenceT1[ + G_TUPLE1 ~func() O.Option[T.Tuple1[T1]], + G_T1 ~func() O.Option[T1], + T1 any]( + t1 G_T1, +) G_TUPLE1 { + return A.SequenceT1( + Map[G_T1, G_TUPLE1, T1, T.Tuple1[T1]], + t1, + ) +} + +// SequenceTuple1 converts a [T.Tuple1[func() O.Option[T]]] into a [func() O.Option[T.Tuple1[T1]]] +func SequenceTuple1[ + G_TUPLE1 ~func() O.Option[T.Tuple1[T1]], + G_T1 ~func() O.Option[T1], + T1 any](t T.Tuple1[G_T1]) G_TUPLE1 { + return A.SequenceTuple1( + Map[G_T1, G_TUPLE1, T1, T.Tuple1[T1]], + t) +} + +// TraverseTuple1 converts a [T.Tuple1[func() O.Option[T]]] into a [func() O.Option[T.Tuple1[T1]]] +func TraverseTuple1[ + G_TUPLE1 ~func() O.Option[T.Tuple1[T1]], + F1 ~func(A1) G_T1, + G_T1 ~func() O.Option[T1], + A1, T1 any](f1 F1) func(T.Tuple1[A1]) G_TUPLE1 { + return func(t T.Tuple1[A1]) G_TUPLE1 { + return A.TraverseTuple1( + Map[G_T1, G_TUPLE1, T1, T.Tuple1[T1]], + f1, + t) + } +} + +// SequenceT2 converts 2 [func() O.Option[T]] into a [func() O.Option[T.Tuple2[T1, T2]]] +func SequenceT2[ + G_TUPLE2 ~func() O.Option[T.Tuple2[T1, T2]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + T1, T2 any]( + t1 G_T1, + t2 G_T2, +) G_TUPLE2 { + return A.SequenceT2( + Map[G_T1, func() O.Option[func(T2) T.Tuple2[T1, T2]], T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() O.Option[func(T2) T.Tuple2[T1, T2]], G_T2], + t1, + t2, + ) +} + +// SequenceTuple2 converts a [T.Tuple2[func() O.Option[T]]] into a [func() O.Option[T.Tuple2[T1, T2]]] +func SequenceTuple2[ + G_TUPLE2 ~func() O.Option[T.Tuple2[T1, T2]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + T1, T2 any](t T.Tuple2[G_T1, G_T2]) G_TUPLE2 { + return A.SequenceTuple2( + Map[G_T1, func() O.Option[func(T2) T.Tuple2[T1, T2]], T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() O.Option[func(T2) T.Tuple2[T1, T2]], G_T2], + t) +} + +// TraverseTuple2 converts a [T.Tuple2[func() O.Option[T]]] into a [func() O.Option[T.Tuple2[T1, T2]]] +func TraverseTuple2[ + G_TUPLE2 ~func() O.Option[T.Tuple2[T1, T2]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + A1, A2, T1, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) G_TUPLE2 { + return func(t T.Tuple2[A1, A2]) G_TUPLE2 { + return A.TraverseTuple2( + Map[G_T1, func() O.Option[func(T2) T.Tuple2[T1, T2]], T1, func(T2) T.Tuple2[T1, T2]], + Ap[G_TUPLE2, func() O.Option[func(T2) T.Tuple2[T1, T2]], G_T2], + f1, + f2, + t) + } +} + +// SequenceT3 converts 3 [func() O.Option[T]] into a [func() O.Option[T.Tuple3[T1, T2, T3]]] +func SequenceT3[ + G_TUPLE3 ~func() O.Option[T.Tuple3[T1, T2, T3]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + T1, T2, T3 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, +) G_TUPLE3 { + return A.SequenceT3( + Map[G_T1, func() O.Option[func(T2) func(T3) T.Tuple3[T1, T2, T3]], T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() O.Option[func(T3) T.Tuple3[T1, T2, T3]], func() O.Option[func(T2) func(T3) T.Tuple3[T1, T2, T3]], G_T2], + Ap[G_TUPLE3, func() O.Option[func(T3) T.Tuple3[T1, T2, T3]], G_T3], + t1, + t2, + t3, + ) +} + +// SequenceTuple3 converts a [T.Tuple3[func() O.Option[T]]] into a [func() O.Option[T.Tuple3[T1, T2, T3]]] +func SequenceTuple3[ + G_TUPLE3 ~func() O.Option[T.Tuple3[T1, T2, T3]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + T1, T2, T3 any](t T.Tuple3[G_T1, G_T2, G_T3]) G_TUPLE3 { + return A.SequenceTuple3( + Map[G_T1, func() O.Option[func(T2) func(T3) T.Tuple3[T1, T2, T3]], T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() O.Option[func(T3) T.Tuple3[T1, T2, T3]], func() O.Option[func(T2) func(T3) T.Tuple3[T1, T2, T3]], G_T2], + Ap[G_TUPLE3, func() O.Option[func(T3) T.Tuple3[T1, T2, T3]], G_T3], + t) +} + +// TraverseTuple3 converts a [T.Tuple3[func() O.Option[T]]] into a [func() O.Option[T.Tuple3[T1, T2, T3]]] +func TraverseTuple3[ + G_TUPLE3 ~func() O.Option[T.Tuple3[T1, T2, T3]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + A1, A2, A3, T1, T2, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) G_TUPLE3 { + return func(t T.Tuple3[A1, A2, A3]) G_TUPLE3 { + return A.TraverseTuple3( + Map[G_T1, func() O.Option[func(T2) func(T3) T.Tuple3[T1, T2, T3]], T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func() O.Option[func(T3) T.Tuple3[T1, T2, T3]], func() O.Option[func(T2) func(T3) T.Tuple3[T1, T2, T3]], G_T2], + Ap[G_TUPLE3, func() O.Option[func(T3) T.Tuple3[T1, T2, T3]], G_T3], + f1, + f2, + f3, + t) + } +} + +// SequenceT4 converts 4 [func() O.Option[T]] into a [func() O.Option[T.Tuple4[T1, T2, T3, T4]]] +func SequenceT4[ + G_TUPLE4 ~func() O.Option[T.Tuple4[T1, T2, T3, T4]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + T1, T2, T3, T4 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, +) G_TUPLE4 { + return A.SequenceT4( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() O.Option[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func() O.Option[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T2], + Ap[func() O.Option[func(T4) T.Tuple4[T1, T2, T3, T4]], func() O.Option[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T3], + Ap[G_TUPLE4, func() O.Option[func(T4) T.Tuple4[T1, T2, T3, T4]], G_T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceTuple4 converts a [T.Tuple4[func() O.Option[T]]] into a [func() O.Option[T.Tuple4[T1, T2, T3, T4]]] +func SequenceTuple4[ + G_TUPLE4 ~func() O.Option[T.Tuple4[T1, T2, T3, T4]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + T1, T2, T3, T4 any](t T.Tuple4[G_T1, G_T2, G_T3, G_T4]) G_TUPLE4 { + return A.SequenceTuple4( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() O.Option[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func() O.Option[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T2], + Ap[func() O.Option[func(T4) T.Tuple4[T1, T2, T3, T4]], func() O.Option[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T3], + Ap[G_TUPLE4, func() O.Option[func(T4) T.Tuple4[T1, T2, T3, T4]], G_T4], + t) +} + +// TraverseTuple4 converts a [T.Tuple4[func() O.Option[T]]] into a [func() O.Option[T.Tuple4[T1, T2, T3, T4]]] +func TraverseTuple4[ + G_TUPLE4 ~func() O.Option[T.Tuple4[T1, T2, T3, T4]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + A1, A2, A3, A4, T1, T2, T3, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) G_TUPLE4 { + return func(t T.Tuple4[A1, A2, A3, A4]) G_TUPLE4 { + return A.TraverseTuple4( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func() O.Option[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], func() O.Option[func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T2], + Ap[func() O.Option[func(T4) T.Tuple4[T1, T2, T3, T4]], func() O.Option[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], G_T3], + Ap[G_TUPLE4, func() O.Option[func(T4) T.Tuple4[T1, T2, T3, T4]], G_T4], + f1, + f2, + f3, + f4, + t) + } +} + +// SequenceT5 converts 5 [func() O.Option[T]] into a [func() O.Option[T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceT5[ + G_TUPLE5 ~func() O.Option[T.Tuple5[T1, T2, T3, T4, T5]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + T1, T2, T3, T4, T5 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, +) G_TUPLE5 { + return A.SequenceT5( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() O.Option[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T2], + Ap[func() O.Option[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T3], + Ap[func() O.Option[func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T4], + Ap[G_TUPLE5, func() O.Option[func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceTuple5 converts a [T.Tuple5[func() O.Option[T]]] into a [func() O.Option[T.Tuple5[T1, T2, T3, T4, T5]]] +func SequenceTuple5[ + G_TUPLE5 ~func() O.Option[T.Tuple5[T1, T2, T3, T4, T5]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + T1, T2, T3, T4, T5 any](t T.Tuple5[G_T1, G_T2, G_T3, G_T4, G_T5]) G_TUPLE5 { + return A.SequenceTuple5( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() O.Option[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T2], + Ap[func() O.Option[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T3], + Ap[func() O.Option[func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T4], + Ap[G_TUPLE5, func() O.Option[func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T5], + t) +} + +// TraverseTuple5 converts a [T.Tuple5[func() O.Option[T]]] into a [func() O.Option[T.Tuple5[T1, T2, T3, T4, T5]]] +func TraverseTuple5[ + G_TUPLE5 ~func() O.Option[T.Tuple5[T1, T2, T3, T4, T5]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + A1, A2, A3, A4, A5, T1, T2, T3, T4, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) G_TUPLE5 { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) G_TUPLE5 { + return A.TraverseTuple5( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func() O.Option[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T2], + Ap[func() O.Option[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T3], + Ap[func() O.Option[func(T5) T.Tuple5[T1, T2, T3, T4, T5]], func() O.Option[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T4], + Ap[G_TUPLE5, func() O.Option[func(T5) T.Tuple5[T1, T2, T3, T4, T5]], G_T5], + f1, + f2, + f3, + f4, + f5, + t) + } +} + +// SequenceT6 converts 6 [func() O.Option[T]] into a [func() O.Option[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceT6[ + G_TUPLE6 ~func() O.Option[T.Tuple6[T1, T2, T3, T4, T5, T6]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + T1, T2, T3, T4, T5, T6 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, +) G_TUPLE6 { + return A.SequenceT6( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T3], + Ap[func() O.Option[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T4], + Ap[func() O.Option[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T5], + Ap[G_TUPLE6, func() O.Option[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceTuple6 converts a [T.Tuple6[func() O.Option[T]]] into a [func() O.Option[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func SequenceTuple6[ + G_TUPLE6 ~func() O.Option[T.Tuple6[T1, T2, T3, T4, T5, T6]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + T1, T2, T3, T4, T5, T6 any](t T.Tuple6[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6]) G_TUPLE6 { + return A.SequenceTuple6( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T3], + Ap[func() O.Option[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T4], + Ap[func() O.Option[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T5], + Ap[G_TUPLE6, func() O.Option[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T6], + t) +} + +// TraverseTuple6 converts a [T.Tuple6[func() O.Option[T]]] into a [func() O.Option[T.Tuple6[T1, T2, T3, T4, T5, T6]]] +func TraverseTuple6[ + G_TUPLE6 ~func() O.Option[T.Tuple6[T1, T2, T3, T4, T5, T6]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + A1, A2, A3, A4, A5, A6, T1, T2, T3, T4, T5, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) G_TUPLE6 { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) G_TUPLE6 { + return A.TraverseTuple6( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T3], + Ap[func() O.Option[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T4], + Ap[func() O.Option[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], func() O.Option[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T5], + Ap[G_TUPLE6, func() O.Option[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], G_T6], + f1, + f2, + f3, + f4, + f5, + f6, + t) + } +} + +// SequenceT7 converts 7 [func() O.Option[T]] into a [func() O.Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceT7[ + G_TUPLE7 ~func() O.Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + T1, T2, T3, T4, T5, T6, T7 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, +) G_TUPLE7 { + return A.SequenceT7( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T4], + Ap[func() O.Option[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T5], + Ap[func() O.Option[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T6], + Ap[G_TUPLE7, func() O.Option[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceTuple7 converts a [T.Tuple7[func() O.Option[T]]] into a [func() O.Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func SequenceTuple7[ + G_TUPLE7 ~func() O.Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7]) G_TUPLE7 { + return A.SequenceTuple7( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T4], + Ap[func() O.Option[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T5], + Ap[func() O.Option[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T6], + Ap[G_TUPLE7, func() O.Option[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T7], + t) +} + +// TraverseTuple7 converts a [T.Tuple7[func() O.Option[T]]] into a [func() O.Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]]] +func TraverseTuple7[ + G_TUPLE7 ~func() O.Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + A1, A2, A3, A4, A5, A6, A7, T1, T2, T3, T4, T5, T6, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) G_TUPLE7 { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) G_TUPLE7 { + return A.TraverseTuple7( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T4], + Ap[func() O.Option[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T5], + Ap[func() O.Option[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], func() O.Option[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T6], + Ap[G_TUPLE7, func() O.Option[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], G_T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t) + } +} + +// SequenceT8 converts 8 [func() O.Option[T]] into a [func() O.Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceT8[ + G_TUPLE8 ~func() O.Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + T1, T2, T3, T4, T5, T6, T7, T8 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, +) G_TUPLE8 { + return A.SequenceT8( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T5], + Ap[func() O.Option[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T6], + Ap[func() O.Option[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T7], + Ap[G_TUPLE8, func() O.Option[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceTuple8 converts a [T.Tuple8[func() O.Option[T]]] into a [func() O.Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func SequenceTuple8[ + G_TUPLE8 ~func() O.Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8]) G_TUPLE8 { + return A.SequenceTuple8( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T5], + Ap[func() O.Option[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T6], + Ap[func() O.Option[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T7], + Ap[G_TUPLE8, func() O.Option[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T8], + t) +} + +// TraverseTuple8 converts a [T.Tuple8[func() O.Option[T]]] into a [func() O.Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]] +func TraverseTuple8[ + G_TUPLE8 ~func() O.Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + A1, A2, A3, A4, A5, A6, A7, A8, T1, T2, T3, T4, T5, T6, T7, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) G_TUPLE8 { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) G_TUPLE8 { + return A.TraverseTuple8( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T5], + Ap[func() O.Option[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T6], + Ap[func() O.Option[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], func() O.Option[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T7], + Ap[G_TUPLE8, func() O.Option[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], G_T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t) + } +} + +// SequenceT9 converts 9 [func() O.Option[T]] into a [func() O.Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceT9[ + G_TUPLE9 ~func() O.Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + G_T9 ~func() O.Option[T9], + T1, T2, T3, T4, T5, T6, T7, T8, T9 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, + t9 G_T9, +) G_TUPLE9 { + return A.SequenceT9( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T5], + Ap[func() O.Option[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T6], + Ap[func() O.Option[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T7], + Ap[func() O.Option[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T8], + Ap[G_TUPLE9, func() O.Option[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceTuple9 converts a [T.Tuple9[func() O.Option[T]]] into a [func() O.Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func SequenceTuple9[ + G_TUPLE9 ~func() O.Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + G_T9 ~func() O.Option[T9], + T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8, G_T9]) G_TUPLE9 { + return A.SequenceTuple9( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T5], + Ap[func() O.Option[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T6], + Ap[func() O.Option[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T7], + Ap[func() O.Option[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T8], + Ap[G_TUPLE9, func() O.Option[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T9], + t) +} + +// TraverseTuple9 converts a [T.Tuple9[func() O.Option[T]]] into a [func() O.Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]] +func TraverseTuple9[ + G_TUPLE9 ~func() O.Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + F9 ~func(A9) G_T9, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + G_T9 ~func() O.Option[T9], + A1, A2, A3, A4, A5, A6, A7, A8, A9, T1, T2, T3, T4, T5, T6, T7, T8, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) G_TUPLE9 { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) G_TUPLE9 { + return A.TraverseTuple9( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T5], + Ap[func() O.Option[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T6], + Ap[func() O.Option[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T7], + Ap[func() O.Option[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], func() O.Option[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T8], + Ap[G_TUPLE9, func() O.Option[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], G_T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t) + } +} + +// SequenceT10 converts 10 [func() O.Option[T]] into a [func() O.Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceT10[ + G_TUPLE10 ~func() O.Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + G_T9 ~func() O.Option[T9], + G_T10 ~func() O.Option[T10], + T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any]( + t1 G_T1, + t2 G_T2, + t3 G_T3, + t4 G_T4, + t5 G_T5, + t6 G_T6, + t7 G_T7, + t8 G_T8, + t9 G_T9, + t10 G_T10, +) G_TUPLE10 { + return A.SequenceT10( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T5], + Ap[func() O.Option[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T6], + Ap[func() O.Option[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T7], + Ap[func() O.Option[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T8], + Ap[func() O.Option[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T9], + Ap[G_TUPLE10, func() O.Option[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceTuple10 converts a [T.Tuple10[func() O.Option[T]]] into a [func() O.Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func SequenceTuple10[ + G_TUPLE10 ~func() O.Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + G_T9 ~func() O.Option[T9], + G_T10 ~func() O.Option[T10], + T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[G_T1, G_T2, G_T3, G_T4, G_T5, G_T6, G_T7, G_T8, G_T9, G_T10]) G_TUPLE10 { + return A.SequenceTuple10( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T5], + Ap[func() O.Option[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T6], + Ap[func() O.Option[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T7], + Ap[func() O.Option[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T8], + Ap[func() O.Option[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T9], + Ap[G_TUPLE10, func() O.Option[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T10], + t) +} + +// TraverseTuple10 converts a [T.Tuple10[func() O.Option[T]]] into a [func() O.Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]] +func TraverseTuple10[ + G_TUPLE10 ~func() O.Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + F1 ~func(A1) G_T1, + F2 ~func(A2) G_T2, + F3 ~func(A3) G_T3, + F4 ~func(A4) G_T4, + F5 ~func(A5) G_T5, + F6 ~func(A6) G_T6, + F7 ~func(A7) G_T7, + F8 ~func(A8) G_T8, + F9 ~func(A9) G_T9, + F10 ~func(A10) G_T10, + G_T1 ~func() O.Option[T1], + G_T2 ~func() O.Option[T2], + G_T3 ~func() O.Option[T3], + G_T4 ~func() O.Option[T4], + G_T5 ~func() O.Option[T5], + G_T6 ~func() O.Option[T6], + G_T7 ~func() O.Option[T7], + G_T8 ~func() O.Option[T8], + G_T9 ~func() O.Option[T9], + G_T10 ~func() O.Option[T10], + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) G_TUPLE10 { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) G_TUPLE10 { + return A.TraverseTuple10( + Map[G_T1, func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T2], + Ap[func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T3], + Ap[func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T4], + Ap[func() O.Option[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T5], + Ap[func() O.Option[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T6], + Ap[func() O.Option[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T7], + Ap[func() O.Option[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T8], + Ap[func() O.Option[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], func() O.Option[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T9], + Ap[G_TUPLE10, func() O.Option[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], G_T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t) + } +} diff --git a/v2/iooption/generic/iooption.go b/v2/iooption/generic/iooption.go new file mode 100644 index 0000000..751bd43 --- /dev/null +++ b/v2/iooption/generic/iooption.go @@ -0,0 +1,261 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "time" + + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + FI "github.com/IBM/fp-go/v2/internal/fromio" + "github.com/IBM/fp-go/v2/internal/optiont" + IO "github.com/IBM/fp-go/v2/io/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// type IOOption[A any] = func() Option[A] + +func MakeIO[GA ~func() O.Option[A], A any](f GA) GA { + return f +} + +func Of[GA ~func() O.Option[A], A any](r A) GA { + return MakeIO(optiont.Of(IO.MonadOf[GA, O.Option[A]], r)) +} + +func Some[GA ~func() O.Option[A], A any](r A) GA { + return Of[GA](r) +} + +func None[GA ~func() O.Option[A], A any]() GA { + return MakeIO(optiont.None(IO.MonadOf[GA, O.Option[A]])) +} + +func MonadOf[GA ~func() O.Option[A], A any](r A) GA { + return Of[GA](r) +} + +func FromIO[GA ~func() O.Option[A], GR ~func() A, A any](mr GR) GA { + return MakeIO(optiont.OfF(IO.MonadMap[GR, GA, A, O.Option[A]], mr)) +} + +func FromOption[GA ~func() O.Option[A], A any](o O.Option[A]) GA { + return IO.Of[GA](o) +} + +func FromEither[GA ~func() O.Option[A], E, A any](e ET.Either[E, A]) GA { + return F.Pipe2( + e, + ET.ToOption[E, A], + FromOption[GA], + ) +} + +func MonadMap[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) B) GB { + return optiont.MonadMap(IO.MonadMap[GA, GB, O.Option[A], O.Option[B]], fa, f) +} + +func Map[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) B) func(GA) GB { + return optiont.Map(IO.Map[GA, GB, O.Option[A], O.Option[B]], f) +} + +func MonadChain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f func(A) GB) GB { + return optiont.MonadChain(IO.MonadChain[GA, GB, O.Option[A], O.Option[B]], IO.MonadOf[GB, O.Option[B]], fa, f) +} + +// MonadChainFirst runs the monad returned by the function but returns the result of the original monad +func MonadChainFirst[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](ma GA, f func(A) GB) GA { + return C.MonadChainFirst( + MonadChain[GA, GA, A, A], + MonadMap[GB, GA, B, A], + ma, + f, + ) +} + +// ChainFirst runs the monad returned by the function but returns the result of the original monad +func ChainFirst[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) GB) func(GA) GA { + return C.ChainFirst( + Chain[GA, GA, A, A], + Map[GB, GA, B, A], + f, + ) +} + +// MonadChainFirstIOK runs the monad returned by the function but returns the result of the original monad +func MonadChainFirstIOK[GA ~func() O.Option[A], GIOB ~func() B, A, B any](first GA, f func(A) GIOB) GA { + return FI.MonadChainFirstIOK( + MonadChain[GA, GA, A, A], + MonadMap[func() O.Option[B], GA, B, A], + FromIO[func() O.Option[B], GIOB, B], + first, + f, + ) +} + +// ChainFirstIOK runs the monad returned by the function but returns the result of the original monad +func ChainFirstIOK[GA ~func() O.Option[A], GIOB ~func() B, A, B any](f func(A) GIOB) func(GA) GA { + return FI.ChainFirstIOK( + Chain[GA, GA, A, A], + Map[func() O.Option[B], GA, B, A], + FromIO[func() O.Option[B], GIOB, B], + f, + ) +} + +func Chain[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) GB) func(GA) GB { + return optiont.Chain(IO.Chain[GA, GB, O.Option[A], O.Option[B]], IO.Of[GB, O.Option[B]], f) +} + +func MonadChainOptionK[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](ma GA, f func(A) O.Option[B]) GB { + return optiont.MonadChainOptionK( + IO.MonadChain[GA, GB, O.Option[A], O.Option[B]], + FromOption[GB, B], + ma, + f, + ) +} + +func ChainOptionK[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) O.Option[B]) func(GA) GB { + return optiont.ChainOptionK( + IO.Chain[GA, GB, O.Option[A], O.Option[B]], + FromOption[GB, B], + f, + ) +} + +func MonadChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B, A, B any](ma GA, f func(A) GR) GB { + return FI.MonadChainIOK( + MonadChain[GA, GB, A, B], + FromIO[GB, GR, B], + ma, + f, + ) +} + +func ChainIOK[GA ~func() O.Option[A], GB ~func() O.Option[B], GR ~func() B, A, B any](f func(A) GR) func(GA) GB { + return FI.ChainIOK( + Chain[GA, GB, A, B], + FromIO[GB, GR, B], + f, + ) +} + +func MonadAp[GB ~func() O.Option[B], GAB ~func() O.Option[func(A) B], GA ~func() O.Option[A], A, B any](mab GAB, ma GA) GB { + return optiont.MonadAp( + IO.MonadAp[GA, GB, func() func(O.Option[A]) O.Option[B], O.Option[A], O.Option[B]], + IO.MonadMap[GAB, func() func(O.Option[A]) O.Option[B], O.Option[func(A) B], func(O.Option[A]) O.Option[B]], + mab, ma) +} + +func Ap[GB ~func() O.Option[B], GAB ~func() O.Option[func(A) B], GA ~func() O.Option[A], A, B any](ma GA) func(GAB) GB { + return optiont.Ap( + IO.Ap[GB, func() func(O.Option[A]) O.Option[B], GA, O.Option[B], O.Option[A]], + IO.Map[GAB, func() func(O.Option[A]) O.Option[B], O.Option[func(A) B], func(O.Option[A]) O.Option[B]], + ma) +} + +func Flatten[GA ~func() O.Option[A], GAA ~func() O.Option[GA], A any](mma GAA) GA { + return MonadChain(mma, F.Identity[GA]) +} + +func Optionize0[GA ~func() O.Option[A], A any](f func() (A, bool)) func() GA { + ef := O.Optionize0(f) + return func() GA { + return MakeIO[GA](ef) + } +} + +func Optionize1[GA ~func() O.Option[A], T1, A any](f func(t1 T1) (A, bool)) func(T1) GA { + ef := O.Optionize1(f) + return func(t1 T1) GA { + return MakeIO[GA](func() O.Option[A] { + return ef(t1) + }) + } +} + +func Optionize2[GA ~func() O.Option[A], T1, T2, A any](f func(t1 T1, t2 T2) (A, bool)) func(T1, T2) GA { + ef := O.Optionize2(f) + return func(t1 T1, t2 T2) GA { + return MakeIO[GA](func() O.Option[A] { + return ef(t1, t2) + }) + } +} + +func Optionize3[GA ~func() O.Option[A], T1, T2, T3, A any](f func(t1 T1, t2 T2, t3 T3) (A, bool)) func(T1, T2, T3) GA { + ef := O.Optionize3(f) + return func(t1 T1, t2 T2, t3 T3) GA { + return MakeIO[GA](func() O.Option[A] { + return ef(t1, t2, t3) + }) + } +} + +func Optionize4[GA ~func() O.Option[A], T1, T2, T3, T4, A any](f func(t1 T1, t2 T2, t3 T3, t4 T4) (A, bool)) func(T1, T2, T3, T4) GA { + ef := O.Optionize4(f) + return func(t1 T1, t2 T2, t3 T3, t4 T4) GA { + return MakeIO[GA](func() O.Option[A] { + return ef(t1, t2, t3, t4) + }) + } +} + +// Memoize computes the value of the provided IO monad lazily but exactly once +func Memoize[GA ~func() O.Option[A], A any](ma GA) GA { + return IO.Memoize(ma) +} + +// Delay creates an operation that passes in the value after some delay +func Delay[GA ~func() O.Option[A], A any](delay time.Duration) func(GA) GA { + return IO.Delay[GA](delay) +} + +// After creates an operation that passes after the given [time.Time] +func After[GA ~func() O.Option[A], A any](timestamp time.Time) func(GA) GA { + return IO.After[GA](timestamp) +} + +// Fold convers an IOOption into an IO +func Fold[GA ~func() O.Option[A], GB ~func() B, A, B any](onNone func() GB, onSome func(A) GB) func(GA) GB { + return optiont.MatchE(IO.MonadChain[GA, GB, O.Option[A], B], onNone, onSome) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[GA ~func() O.Option[A], A any](gen func() GA) GA { + return IO.Defer[GA](gen) +} + +func MonadAlt[LAZY ~func() GIOA, GIOA ~func() O.Option[A], A any](first GIOA, second LAZY) GIOA { + return optiont.MonadAlt( + IO.MonadOf[GIOA], + IO.MonadChain[GIOA, GIOA], + + first, + second, + ) +} + +func Alt[LAZY ~func() GIOA, GIOA ~func() O.Option[A], A any](second LAZY) func(GIOA) GIOA { + return optiont.Alt( + IO.Of[GIOA], + IO.Chain[GIOA, GIOA], + + second, + ) +} diff --git a/v2/iooption/generic/resource.go b/v2/iooption/generic/resource.go new file mode 100644 index 0000000..e69f050 --- /dev/null +++ b/v2/iooption/generic/resource.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[ + GA ~func() O.Option[A], + GR ~func() O.Option[R], + GANY ~func() O.Option[ANY], + R, A, ANY any](onCreate GR, onRelease func(R) GANY) func(func(R) GA) GA { + // simply map to implementation of bracket + return F.Bind13of3(Bracket[GR, GA, GANY, R, A, ANY])(onCreate, F.Ignore2of2[O.Option[A]](onRelease)) +} diff --git a/v2/iooption/generic/retry.go b/v2/iooption/generic/retry.go new file mode 100644 index 0000000..a0aed0f --- /dev/null +++ b/v2/iooption/generic/retry.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + O "github.com/IBM/fp-go/v2/option" + R "github.com/IBM/fp-go/v2/retry" + G "github.com/IBM/fp-go/v2/retry/generic" +) + +// Retry combinator for actions that don't raise exceptions, but +// signal in their type the outcome has failed. Examples are the +// `Option`, `Either` and `EitherT` monads. +func Retrying[GA ~func() O.Option[A], A any]( + policy R.RetryPolicy, + action func(R.RetryStatus) GA, + check func(A) bool, +) GA { + // get an implementation for the types + return G.Retrying( + Chain[GA, GA, A, A], + Chain[func() O.Option[R.RetryStatus], GA, R.RetryStatus, A], + Of[GA, A], + Of[func() O.Option[R.RetryStatus], R.RetryStatus], + Delay[func() O.Option[R.RetryStatus], R.RetryStatus], + + policy, action, check) +} diff --git a/v2/iooption/generic/sync.go b/v2/iooption/generic/sync.go new file mode 100644 index 0000000..12da977 --- /dev/null +++ b/v2/iooption/generic/sync.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + G "github.com/IBM/fp-go/v2/io/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[GA ~func() O.Option[A], A any](lock func() context.CancelFunc) func(fa GA) GA { + return G.WithLock[GA](lock) +} diff --git a/v2/iooption/generic/traverse.go b/v2/iooption/generic/traverse.go new file mode 100644 index 0000000..60c2072 --- /dev/null +++ b/v2/iooption/generic/traverse.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/io/generic" + O "github.com/IBM/fp-go/v2/option" +) + +func TraverseArray[TB ~func() O.Option[B], TBS ~func() O.Option[GB], GA ~[]A, GB ~[]B, A, B any](f func(A) TB) func(GA) TBS { + return F.Flow2( + I.TraverseArray[TB, func() []O.Option[B], GA](f), + I.Map[func() []O.Option[B], TBS](O.SequenceArrayG[GB, []O.Option[B], B]), + ) +} + +func TraverseArrayWithIndex[TB ~func() O.Option[B], TBS ~func() O.Option[GB], GA ~[]A, GB ~[]B, A, B any](f func(int, A) TB) func(GA) TBS { + return F.Flow2( + I.TraverseArrayWithIndex[TB, func() []O.Option[B], GA](f), + I.Map[func() []O.Option[B], TBS](O.SequenceArrayG[GB, []O.Option[B], B]), + ) +} + +func SequenceArray[TB ~func() O.Option[B], TBS ~func() O.Option[GB], GA ~[]TB, GB ~[]B, A, B any](ma GA) TBS { + return TraverseArray[TB, TBS, GA](F.Identity[TB])(ma) +} diff --git a/v2/iooption/iooption.go b/v2/iooption/iooption.go new file mode 100644 index 0000000..9fda43d --- /dev/null +++ b/v2/iooption/iooption.go @@ -0,0 +1,178 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + "time" + + ET "github.com/IBM/fp-go/v2/either" + I "github.com/IBM/fp-go/v2/io" + IO "github.com/IBM/fp-go/v2/io" + G "github.com/IBM/fp-go/v2/iooption/generic" + L "github.com/IBM/fp-go/v2/lazy" + O "github.com/IBM/fp-go/v2/option" +) + +// IO represents a synchronous computation that may fail +// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details +type IOOption[A any] I.IO[O.Option[A]] + +func MakeIO[A any](f IOOption[A]) IOOption[A] { + return G.MakeIO(f) +} + +func Of[A any](r A) IOOption[A] { + return G.Of[IOOption[A]](r) +} + +func Some[A any](r A) IOOption[A] { + return G.Some[IOOption[A]](r) +} + +func None[A any]() IOOption[A] { + return G.None[IOOption[A]]() +} + +func MonadOf[A any](r A) IOOption[A] { + return G.MonadOf[IOOption[A]](r) +} + +func FromOption[A any](o O.Option[A]) IOOption[A] { + return G.FromOption[IOOption[A]](o) +} + +func ChainOptionK[A, B any](f func(A) O.Option[B]) func(IOOption[A]) IOOption[B] { + return G.ChainOptionK[IOOption[A], IOOption[B]](f) +} + +func MonadChainIOK[A, B any](ma IOOption[A], f func(A) I.IO[B]) IOOption[B] { + return G.MonadChainIOK[IOOption[A], IOOption[B]](ma, f) +} + +func ChainIOK[A, B any](f func(A) I.IO[B]) func(IOOption[A]) IOOption[B] { + return G.ChainIOK[IOOption[A], IOOption[B]](f) +} + +func FromIO[A any](mr I.IO[A]) IOOption[A] { + return G.FromIO[IOOption[A]](mr) +} + +func MonadMap[A, B any](fa IOOption[A], f func(A) B) IOOption[B] { + return G.MonadMap[IOOption[A], IOOption[B]](fa, f) +} + +func Map[A, B any](f func(A) B) func(IOOption[A]) IOOption[B] { + return G.Map[IOOption[A], IOOption[B]](f) +} + +func MonadChain[A, B any](fa IOOption[A], f func(A) IOOption[B]) IOOption[B] { + return G.MonadChain(fa, f) +} + +func Chain[A, B any](f func(A) IOOption[B]) func(IOOption[A]) IOOption[B] { + return G.Chain[IOOption[A]](f) +} + +func MonadAp[B, A any](mab IOOption[func(A) B], ma IOOption[A]) IOOption[B] { + return G.MonadAp[IOOption[B]](mab, ma) +} + +func Ap[B, A any](ma IOOption[A]) func(IOOption[func(A) B]) IOOption[B] { + return G.Ap[IOOption[B], IOOption[func(A) B]](ma) +} + +func Flatten[A any](mma IOOption[IOOption[A]]) IOOption[A] { + return G.Flatten(mma) +} + +func Optionize0[A any](f func() (A, bool)) func() IOOption[A] { + return G.Optionize0[IOOption[A]](f) +} + +func Optionize1[T1, A any](f func(t1 T1) (A, bool)) func(T1) IOOption[A] { + return G.Optionize1[IOOption[A]](f) +} + +func Optionize2[T1, T2, A any](f func(t1 T1, t2 T2) (A, bool)) func(T1, T2) IOOption[A] { + return G.Optionize2[IOOption[A]](f) +} + +func Optionize3[T1, T2, T3, A any](f func(t1 T1, t2 T2, t3 T3) (A, bool)) func(T1, T2, T3) IOOption[A] { + return G.Optionize3[IOOption[A]](f) +} + +func Optionize4[T1, T2, T3, T4, A any](f func(t1 T1, t2 T2, t3 T3, t4 T4) (A, bool)) func(T1, T2, T3, T4) IOOption[A] { + return G.Optionize4[IOOption[A]](f) +} + +func Memoize[A any](ma IOOption[A]) IOOption[A] { + return G.Memoize(ma) +} + +// Fold convers an [IOOption] into an [IO] +func Fold[A, B any](onNone func() I.IO[B], onSome func(A) I.IO[B]) func(IOOption[A]) I.IO[B] { + return G.Fold[IOOption[A]](onNone, onSome) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[A any](gen func() IOOption[A]) IOOption[A] { + return G.Defer[IOOption[A]](gen) +} + +// FromEither converts an [Either] into an [IOOption] +func FromEither[E, A any](e ET.Either[E, A]) IOOption[A] { + return G.FromEither[IOOption[A]](e) +} + +// MonadAlt identifies an associative operation on a type constructor +func MonadAlt[A any](first IOOption[A], second L.Lazy[IOOption[A]]) IOOption[A] { + return G.MonadAlt(first, second) +} + +// Alt identifies an associative operation on a type constructor +func Alt[A any](second L.Lazy[IOOption[A]]) func(IOOption[A]) IOOption[A] { + return G.Alt(second) +} + +// MonadChainFirst runs the monad returned by the function but returns the result of the original monad +func MonadChainFirst[A, B any](ma IOOption[A], f func(A) IOOption[B]) IOOption[A] { + return G.MonadChainFirst[IOOption[A], IOOption[B]](ma, f) +} + +// ChainFirst runs the monad returned by the function but returns the result of the original monad +func ChainFirst[A, B any](f func(A) IOOption[B]) func(IOOption[A]) IOOption[A] { + return G.ChainFirst[IOOption[A], IOOption[B]](f) +} + +// MonadChainFirstIOK runs the monad returned by the function but returns the result of the original monad +func MonadChainFirstIOK[A, B any](first IOOption[A], f func(A) IO.IO[B]) IOOption[A] { + return G.MonadChainFirstIOK[IOOption[A], IO.IO[B]](first, f) +} + +// ChainFirstIOK runs the monad returned by the function but returns the result of the original monad +func ChainFirstIOK[A, B any](f func(A) IO.IO[B]) func(IOOption[A]) IOOption[A] { + return G.ChainFirstIOK[IOOption[A], IO.IO[B]](f) +} + +// Delay creates an operation that passes in the value after some delay +func Delay[A any](delay time.Duration) func(IOOption[A]) IOOption[A] { + return G.Delay[IOOption[A]](delay) +} + +// After creates an operation that passes after the given [time.Time] +func After[A any](timestamp time.Time) func(IOOption[A]) IOOption[A] { + return G.After[IOOption[A]](timestamp) +} diff --git a/v2/iooption/iooption_test.go b/v2/iooption/iooption_test.go new file mode 100644 index 0000000..cb94e23 --- /dev/null +++ b/v2/iooption/iooption_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + "fmt" + "os" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + I "github.com/IBM/fp-go/v2/io" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +func TestMap(t *testing.T) { + assert.Equal(t, O.Of(2), F.Pipe1( + Of(1), + Map(utils.Double), + )()) + +} + +func TestChainOptionK(t *testing.T) { + f := ChainOptionK(func(n int) O.Option[int] { + if n > 0 { + return O.Of(n) + } + return O.None[int]() + + }) + assert.Equal(t, O.Of(1), f(Of(1))()) + assert.Equal(t, O.None[int](), f(Of(-1))()) + assert.Equal(t, O.None[int](), f(None[int]())()) +} + +func TestFromOption(t *testing.T) { + f := FromOption[int] + assert.Equal(t, O.Of(1), f(O.Some(1))()) + assert.Equal(t, O.None[int](), f(O.None[int]())()) +} + +func TestChainIOK(t *testing.T) { + f := ChainIOK(func(n int) I.IO[string] { + return I.MakeIO(func() string { + return fmt.Sprintf("%d", n) + }) + }) + + assert.Equal(t, O.Of("1"), f(Of(1))()) + assert.Equal(t, O.None[string](), f(None[int]())()) +} + +func TestEnv(t *testing.T) { + env := Optionize1(os.LookupEnv) + + assert.True(t, O.IsSome(env("PATH")())) + assert.False(t, O.IsSome(env("PATHxyz")())) +} diff --git a/v2/iooption/resource.go b/v2/iooption/resource.go new file mode 100644 index 0000000..1768cc2 --- /dev/null +++ b/v2/iooption/resource.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + G "github.com/IBM/fp-go/v2/iooption/generic" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[ + R, A, ANY any](onCreate IOOption[R], onRelease func(R) IOOption[ANY]) func(func(R) IOOption[A]) IOOption[A] { + // just dispatch + return G.WithResource[IOOption[A], IOOption[R], IOOption[ANY]](onCreate, onRelease) +} diff --git a/v2/iooption/retry.go b/v2/iooption/retry.go new file mode 100644 index 0000000..7145013 --- /dev/null +++ b/v2/iooption/retry.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + G "github.com/IBM/fp-go/v2/iooption/generic" + R "github.com/IBM/fp-go/v2/retry" +) + +// Retrying will retry the actions according to the check policy +func Retrying[A any]( + policy R.RetryPolicy, + action func(R.RetryStatus) IOOption[A], + check func(A) bool, +) IOOption[A] { + return G.Retrying(policy, action, check) +} diff --git a/v2/iooption/sync.go b/v2/iooption/sync.go new file mode 100644 index 0000000..dd27808 --- /dev/null +++ b/v2/iooption/sync.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iooption + +import ( + "context" + + IO "github.com/IBM/fp-go/v2/io" + G "github.com/IBM/fp-go/v2/iooption/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[E, A any](lock IO.IO[context.CancelFunc]) func(fa IOOption[A]) IOOption[A] { + return G.WithLock[IOOption[A]](lock) +} diff --git a/v2/iooption/testing/laws.go b/v2/iooption/testing/laws.go new file mode 100644 index 0000000..9067d59 --- /dev/null +++ b/v2/iooption/testing/laws.go @@ -0,0 +1,74 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" + IOO "github.com/IBM/fp-go/v2/iooption" +) + +// AssertLaws asserts the apply monad laws for the `Either` monad +func AssertLaws[A, B, C any](t *testing.T, + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + return L.AssertLaws(t, + IOO.Eq(eqa), + IOO.Eq(eqb), + IOO.Eq(eqc), + + IOO.Of[A], + IOO.Of[B], + IOO.Of[C], + + IOO.Of[func(A) A], + IOO.Of[func(A) B], + IOO.Of[func(B) C], + IOO.Of[func(func(A) B) B], + + IOO.MonadMap[A, A], + IOO.MonadMap[A, B], + IOO.MonadMap[A, C], + IOO.MonadMap[B, C], + + IOO.MonadMap[func(B) C, func(func(A) B) func(A) C], + + IOO.MonadChain[A, A], + IOO.MonadChain[A, B], + IOO.MonadChain[A, C], + IOO.MonadChain[B, C], + + IOO.MonadAp[A, A], + IOO.MonadAp[B, A], + IOO.MonadAp[C, B], + IOO.MonadAp[C, A], + + IOO.MonadAp[B, func(A) B], + IOO.MonadAp[func(A) C, func(A) B], + + ab, + bc, + ) + +} diff --git a/v2/iooption/testing/laws_test.go b/v2/iooption/testing/laws_test.go new file mode 100644 index 0000000..43c0f37 --- /dev/null +++ b/v2/iooption/testing/laws_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqa, eqb, eqc, ab, bc) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/iterator/stateless/any.go b/v2/iterator/stateless/any.go new file mode 100644 index 0000000..978718b --- /dev/null +++ b/v2/iterator/stateless/any.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// Any returns `true` if any element of the iterable is `true`. If the iterable is empty, return `false` +// Similar to the [https://docs.python.org/3/library/functions.html#any] function +func Any[U any](pred func(U) bool) func(ma Iterator[U]) bool { + return G.Any[Iterator[U]](pred) +} diff --git a/v2/iterator/stateless/any_test.go b/v2/iterator/stateless/any_test.go new file mode 100644 index 0000000..6d71fe8 --- /dev/null +++ b/v2/iterator/stateless/any_test.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestAny(t *testing.T) { + + anyBool := Any(F.Identity[bool]) + + i1 := FromArray(A.From(false, true, false)) + assert.True(t, anyBool(i1)) + + i2 := FromArray(A.From(false, false, false)) + assert.False(t, anyBool(i2)) + + i3 := Empty[bool]() + assert.False(t, anyBool(i3)) +} diff --git a/v2/iterator/stateless/benchmark_test.go b/v2/iterator/stateless/benchmark_test.go new file mode 100644 index 0000000..58a90e7 --- /dev/null +++ b/v2/iterator/stateless/benchmark_test.go @@ -0,0 +1,65 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" +) + +func BenchmarkMulti(b *testing.B) { + // run the Fib function b.N times + for n := 0; n < b.N; n++ { + single() + } +} + +func single() int64 { + + length := 10000 + nums := make([]int, 0, length) + for i := 0; i < length; i++ { + nums = append(nums, i+1) + } + + return F.Pipe6( + nums, + FromArray[int], + Filter(func(n int) bool { + return n%2 == 0 + }), + Map(func(t int) int64 { + return int64(t) + }), + Filter(func(t int64) bool { + n := t + for n/10 != 0 { + if n%10 == 4 { + return false + } + n = n / 10 + } + return true + }), + Map(func(t int64) int { + return int(t) + }), + Reduce(func(n int64, r int) int64 { + return n + int64(r) + }, int64(0)), + ) +} diff --git a/v2/iterator/stateless/bind.go b/v2/iterator/stateless/bind.go new file mode 100644 index 0000000..ab3ee87 --- /dev/null +++ b/v2/iterator/stateless/bind.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) Iterator[S] { + return G.Do[Iterator[S], S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) Iterator[T], +) func(Iterator[S1]) Iterator[S2] { + return G.Bind[Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(Iterator[S1]) Iterator[S2] { + return G.Let[Iterator[S1], Iterator[S2], S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(Iterator[S1]) Iterator[S2] { + return G.LetTo[Iterator[S1], Iterator[S2], S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func(Iterator[T]) Iterator[S1] { + return G.BindTo[Iterator[S1], Iterator[T], S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa Iterator[T], +) func(Iterator[S1]) Iterator[S2] { + return G.ApS[Iterator[func(T) S2], Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, fa) +} diff --git a/v2/iterator/stateless/bind_test.go b/v2/iterator/stateless/bind_test.go new file mode 100644 index 0000000..b237151 --- /dev/null +++ b/v2/iterator/stateless/bind_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) Iterator[string] { + return Of("Doe") +} + +func getGivenName(s utils.WithLastName) Iterator[string] { + return Of("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map(utils.GetFullName), + ) + + assert.Equal(t, ToArray(res), A.Of("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + ApS(utils.SetLastName, Of("Doe")), + ApS(utils.SetGivenName, Of("John")), + Map(utils.GetFullName), + ) + + assert.Equal(t, ToArray(res), A.Of("John Doe")) +} diff --git a/v2/iterator/stateless/compress.go b/v2/iterator/stateless/compress.go new file mode 100644 index 0000000..58738d0 --- /dev/null +++ b/v2/iterator/stateless/compress.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" + P "github.com/IBM/fp-go/v2/pair" +) + +// Compress returns an [Iterator] that filters elements from a data [Iterator] returning only those that have a corresponding element in selector [Iterator] that evaluates to `true`. +// Stops when either the data or selectors iterator has been exhausted. +func Compress[U any](sel Iterator[bool]) func(Iterator[U]) Iterator[U] { + return G.Compress[Iterator[U], Iterator[bool], Iterator[P.Pair[U, bool]]](sel) +} diff --git a/v2/iterator/stateless/compress_test.go b/v2/iterator/stateless/compress_test.go new file mode 100644 index 0000000..a70b226 --- /dev/null +++ b/v2/iterator/stateless/compress_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + "github.com/stretchr/testify/assert" +) + +func TestCompress(t *testing.T) { + // sequence of 5 items + data := From(0, 1, 2, 3) + // select some of these items + selector := From(true, false, false, true) + // compressed result + compressed := Compress[int](selector)(data) + + assert.Equal(t, A.From(0, 3), ToArray(compressed)) + +} diff --git a/v2/iterator/stateless/cycle.go b/v2/iterator/stateless/cycle.go new file mode 100644 index 0000000..0daf56b --- /dev/null +++ b/v2/iterator/stateless/cycle.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element. +// Note, the [Iterator] does not produce any output until the predicate first becomes false +func Cycle[U any](ma Iterator[U]) Iterator[U] { + return G.Cycle[Iterator[U]](ma) +} diff --git a/v2/iterator/stateless/cycle_test.go b/v2/iterator/stateless/cycle_test.go new file mode 100644 index 0000000..eb41494 --- /dev/null +++ b/v2/iterator/stateless/cycle_test.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + "github.com/stretchr/testify/assert" +) + +func TestCycle(t *testing.T) { + // sequence of 5 items + items := Take[int](5)(Count(0)) + // repeat + repeated := Take[int](17)(Cycle(items)) + + assert.Equal(t, A.From(0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1), ToArray(repeated)) + +} diff --git a/v2/iterator/stateless/doc.go b/v2/iterator/stateless/doc.go new file mode 100644 index 0000000..2843b3d --- /dev/null +++ b/v2/iterator/stateless/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package stateless defines a stateless (pure) iterator, i.e. one that can be iterated over multiple times without +// side effects, it is threadsafe +package stateless diff --git a/v2/iterator/stateless/dropwhile.go b/v2/iterator/stateless/dropwhile.go new file mode 100644 index 0000000..0ba6183 --- /dev/null +++ b/v2/iterator/stateless/dropwhile.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element. +// Note, the [Iterator] does not produce any output until the predicate first becomes false +func DropWhile[U any](pred func(U) bool) func(Iterator[U]) Iterator[U] { + return G.DropWhile[Iterator[U]](pred) +} diff --git a/v2/iterator/stateless/dropwhile_test.go b/v2/iterator/stateless/dropwhile_test.go new file mode 100644 index 0000000..48779e2 --- /dev/null +++ b/v2/iterator/stateless/dropwhile_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + "github.com/stretchr/testify/assert" +) + +func TestDropWhile(t *testing.T) { + // sequence of 5 items + data := Take[int](10)(Cycle(From(0, 1, 2, 3))) + + total := DropWhile(func(data int) bool { + return data <= 2 + })(data) + + assert.Equal(t, A.From(3, 0, 1, 2, 3, 0, 1), ToArray(total)) + +} diff --git a/v2/iterator/stateless/example_test.go b/v2/iterator/stateless/example_test.go new file mode 100644 index 0000000..d34ba74 --- /dev/null +++ b/v2/iterator/stateless/example_test.go @@ -0,0 +1,55 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "fmt" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +func Example_any() { + // `Any` function that simply returns the boolean identity + anyBool := Any(F.Identity[bool]) + + fmt.Println(anyBool(FromArray(A.From(true, false, false)))) + fmt.Println(anyBool(FromArray(A.From(false, false, false)))) + fmt.Println(anyBool(Empty[bool]())) + + // Output: + // true + // false + // false +} + +func Example_next() { + + seq := MakeBy(F.Identity[int]) + + first := seq() + + value := F.Pipe1( + first, + O.Map(Current[int]), + ) + + fmt.Println(value) + + // Output: + // Some[int](0) +} diff --git a/v2/iterator/stateless/first.go b/v2/iterator/stateless/first.go new file mode 100644 index 0000000..82a5088 --- /dev/null +++ b/v2/iterator/stateless/first.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// First returns the first item in an iterator if such an item exists +func First[U any](mu Iterator[U]) O.Option[U] { + return G.First[Iterator[U]](mu) +} diff --git a/v2/iterator/stateless/first_test.go b/v2/iterator/stateless/first_test.go new file mode 100644 index 0000000..5e46e18 --- /dev/null +++ b/v2/iterator/stateless/first_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +func TestFirst(t *testing.T) { + + seq := From(1, 2, 3) + + fst := First(seq) + + assert.Equal(t, O.Of(1), fst) +} + +func TestNoFirst(t *testing.T) { + + seq := Empty[int]() + + fst := First(seq) + + assert.Equal(t, O.None[int](), fst) +} diff --git a/v2/iterator/stateless/generic/any.go b/v2/iterator/stateless/generic/any.go new file mode 100644 index 0000000..d19759f --- /dev/null +++ b/v2/iterator/stateless/generic/any.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// Any returns `true` if any element of the iterable is `true`. If the iterable is empty, return `false` +func Any[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) bool, U any](pred FCT) func(ma GU) bool { + return F.Flow3( + Filter[GU](pred), + First[GU], + O.IsSome[U], + ) +} diff --git a/v2/iterator/stateless/generic/bind.go b/v2/iterator/stateless/generic/bind.go new file mode 100644 index 0000000..a020000 --- /dev/null +++ b/v2/iterator/stateless/generic/bind.go @@ -0,0 +1,92 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GS ~func() O.Option[P.Pair[GS, S]], S any]( + empty S, +) GS { + return Of[GS](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], GA ~func() O.Option[P.Pair[GA, A]], S1, S2, A any]( + setter func(A) func(S1) S2, + f func(S1) GA, +) func(GS1) GS2 { + + return C.Bind( + Chain[GS2, GS1, S1, S2], + Map[GS2, GA, func(A) S2, A, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], S1, S2, A any]( + key func(A) func(S1) S2, + f func(S1) A, +) func(GS1) GS2 { + return F.Let( + Map[GS2, GS1, func(S1) S2, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GS1) GS2 { + return F.LetTo( + Map[GS2, GS1, func(S1) S2, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GS1 ~func() O.Option[P.Pair[GS1, S1]], GA ~func() O.Option[P.Pair[GA, A]], S1, A any]( + setter func(A) S1, +) func(GA) GS1 { + return C.BindTo( + Map[GS1, GA, func(A) S1, A, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GAS2 ~func() O.Option[P.Pair[GAS2, func(A) S2]], GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], GA ~func() O.Option[P.Pair[GA, A]], S1, S2, A any]( + setter func(A) func(S1) S2, + fa GA, +) func(GS1) GS2 { + return apply.ApS( + Ap[GAS2, GS2, GA, A, S2], + Map[GAS2, GS1, func(S1) func(A) S2, S1, func(A) S2], + setter, + fa, + ) +} diff --git a/v2/iterator/stateless/generic/compress.go b/v2/iterator/stateless/generic/compress.go new file mode 100644 index 0000000..aecc45b --- /dev/null +++ b/v2/iterator/stateless/generic/compress.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// Compress returns an [Iterator] that filters elements from a data [Iterator] returning only those that have a corresponding element in selector [Iterator] that evaluates to `true`. +// Stops when either the data or selectors iterator has been exhausted. +func Compress[GU ~func() O.Option[P.Pair[GU, U]], GB ~func() O.Option[P.Pair[GB, bool]], CS ~func() O.Option[P.Pair[CS, P.Pair[U, bool]]], U any](sel GB) func(GU) GU { + return F.Flow2( + Zip[GU, GB, CS](sel), + FilterMap[GU, CS](F.Flow2( + O.FromPredicate(P.Tail[U, bool]), + O.Map(P.Head[U, bool]), + )), + ) +} diff --git a/v2/iterator/stateless/generic/cycle.go b/v2/iterator/stateless/generic/cycle.go new file mode 100644 index 0000000..d9ae923 --- /dev/null +++ b/v2/iterator/stateless/generic/cycle.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +func Cycle[GU ~func() O.Option[P.Pair[GU, U]], U any](ma GU) GU { + // avoid cyclic references + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GU, U]] + + recurse := func(mu GU) GU { + return F.Nullary2( + mu, + m, + ) + } + + m = O.Fold(func() O.Option[P.Pair[GU, U]] { + return recurse(ma)() + }, F.Flow2( + P.BiMap(recurse, F.Identity[U]), + O.Of[P.Pair[GU, U]], + )) + + return recurse(ma) +} diff --git a/v2/iterator/stateless/generic/dropwhile.go b/v2/iterator/stateless/generic/dropwhile.go new file mode 100644 index 0000000..3bcb0b3 --- /dev/null +++ b/v2/iterator/stateless/generic/dropwhile.go @@ -0,0 +1,49 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" + PR "github.com/IBM/fp-go/v2/predicate" +) + +// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element. +// Note, the [Iterator] does not produce any output until the predicate first becomes false +func DropWhile[GU ~func() O.Option[P.Pair[GU, U]], U any](pred func(U) bool) func(GU) GU { + // avoid cyclic references + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GU, U]] + + fromPred := O.FromPredicate(PR.Not(PR.ContraMap(P.Tail[GU, U])(pred))) + + recurse := func(mu GU) GU { + return F.Nullary2( + mu, + m, + ) + } + + m = O.Chain(func(t P.Pair[GU, U]) O.Option[P.Pair[GU, U]] { + return F.Pipe2( + t, + fromPred, + O.Fold(recurse(Next(t)), O.Of[P.Pair[GU, U]]), + ) + }) + + return recurse +} diff --git a/v2/iterator/stateless/generic/first.go b/v2/iterator/stateless/generic/first.go new file mode 100644 index 0000000..47d6d1f --- /dev/null +++ b/v2/iterator/stateless/generic/first.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// First returns the first item in an iterator if such an item exists +func First[GU ~func() O.Option[P.Pair[GU, U]], U any](mu GU) O.Option[U] { + return F.Pipe1( + mu(), + O.Map(P.Tail[GU, U]), + ) +} diff --git a/v2/iterator/stateless/generic/io.go b/v2/iterator/stateless/generic/io.go new file mode 100644 index 0000000..3420d8a --- /dev/null +++ b/v2/iterator/stateless/generic/io.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/io/generic" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// FromLazy returns an iterator on top of a lazy function +func FromLazy[GU ~func() O.Option[P.Pair[GU, U]], LZ ~func() U, U any](l LZ) GU { + return F.Pipe1( + l, + L.Map[LZ, GU](F.Flow2( + F.Bind1st(P.MakePair[GU, U], Empty[GU]()), + O.Of[P.Pair[GU, U]], + )), + ) +} diff --git a/v2/iterator/stateless/generic/iterator.go b/v2/iterator/stateless/generic/iterator.go new file mode 100644 index 0000000..015be83 --- /dev/null +++ b/v2/iterator/stateless/generic/iterator.go @@ -0,0 +1,270 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + A "github.com/IBM/fp-go/v2/array/generic" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + "github.com/IBM/fp-go/v2/internal/utils" + IO "github.com/IBM/fp-go/v2/iooption/generic" + M "github.com/IBM/fp-go/v2/monoid" + N "github.com/IBM/fp-go/v2/number" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// Next returns the iterator for the next element in an iterator `P.Pair` +func Next[GU ~func() O.Option[P.Pair[GU, U]], U any](m P.Pair[GU, U]) GU { + return P.Head(m) +} + +// Current returns the current element in an iterator `P.Pair` +func Current[GU ~func() O.Option[P.Pair[GU, U]], U any](m P.Pair[GU, U]) U { + return P.Tail(m) +} + +// From constructs an array from a set of variadic arguments +func From[GU ~func() O.Option[P.Pair[GU, U]], U any](data ...U) GU { + return FromArray[GU](data) +} + +// Empty returns the empty iterator +func Empty[GU ~func() O.Option[P.Pair[GU, U]], U any]() GU { + return IO.None[GU]() +} + +// Of returns an iterator with one single element +func Of[GU ~func() O.Option[P.Pair[GU, U]], U any](a U) GU { + return IO.Of[GU](P.MakePair(Empty[GU](), a)) +} + +// FromArray returns an iterator from multiple elements +func FromArray[GU ~func() O.Option[P.Pair[GU, U]], US ~[]U, U any](as US) GU { + return A.MatchLeft(Empty[GU], func(head U, tail US) GU { + return func() O.Option[P.Pair[GU, U]] { + return O.Of(P.MakePair(FromArray[GU](tail), head)) + } + })(as) +} + +// reduce applies a function for each value of the iterator with a floating result +func reduce[GU ~func() O.Option[P.Pair[GU, U]], U, V any](as GU, f func(V, U) V, initial V) V { + next, ok := O.Unwrap(as()) + current := initial + for ok { + // next (with bad side effect) + current = f(current, Current(next)) + next, ok = O.Unwrap(Next(next)()) + } + return current +} + +// Reduce applies a function for each value of the iterator with a floating result +func Reduce[GU ~func() O.Option[P.Pair[GU, U]], U, V any](f func(V, U) V, initial V) func(GU) V { + return F.Bind23of3(reduce[GU, U, V])(f, initial) +} + +// ToArray converts the iterator to an array +func ToArray[GU ~func() O.Option[P.Pair[GU, U]], US ~[]U, U any](u GU) US { + return Reduce[GU](A.Append[US], A.Empty[US]())(u) +} + +func Map[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) V, U, V any](f FCT) func(ma GU) GV { + // pre-declare to avoid cyclic reference + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] + + recurse := func(ma GU) GV { + return F.Nullary2( + ma, + m, + ) + } + + m = O.Map(P.BiMap(recurse, f)) + + return recurse +} + +func MonadMap[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](ma GU, f func(U) V) GV { + return Map[GV, GU](f)(ma) +} + +func concat[GU ~func() O.Option[P.Pair[GU, U]], U any](right, left GU) GU { + var m func(ma O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GU, U]] + + recurse := func(left GU) GU { + return F.Nullary2(left, m) + } + + m = O.Fold( + right, + F.Flow2( + P.BiMap(recurse, F.Identity[U]), + O.Some[P.Pair[GU, U]], + )) + + return recurse(left) +} + +func Chain[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](f func(U) GV) func(GU) GV { + // pre-declare to avoid cyclic reference + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] + + recurse := func(ma GU) GV { + return F.Nullary2( + ma, + m, + ) + } + m = O.Chain( + F.Flow3( + P.BiMap(recurse, f), + P.Paired(concat[GV]), + func(v GV) O.Option[P.Pair[GV, V]] { + return v() + }, + ), + ) + + return recurse +} + +func MonadChain[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](ma GU, f func(U) GV) GV { + return Chain[GV, GU](f)(ma) +} + +func MonadChainFirst[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](ma GU, f func(U) GV) GU { + return C.MonadChainFirst( + MonadChain[GU, GU, U, U], + MonadMap[GU, GV, V, U], + ma, + f, + ) +} + +func ChainFirst[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](f func(U) GV) func(GU) GU { + return C.ChainFirst( + Chain[GU, GU, U, U], + Map[GU, GV, func(V) U, V, U], + f, + ) +} + +func Flatten[GV ~func() O.Option[P.Pair[GV, GU]], GU ~func() O.Option[P.Pair[GU, U]], U any](ma GV) GU { + return MonadChain(ma, F.Identity[GU]) +} + +// MakeBy returns an [Iterator] with an infinite number of elements initialized with `f(i)` +func MakeBy[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(int) U, U any](f FCT) GU { + + var m func(int) O.Option[P.Pair[GU, U]] + + recurse := func(i int) GU { + return F.Nullary2( + F.Constant(i), + m, + ) + } + + m = F.Flow3( + P.Of[int], + P.BiMap(F.Flow2( + utils.Inc, + recurse), + f), + O.Of[P.Pair[GU, U]], + ) + + // bootstrap + return recurse(0) +} + +// Replicate creates an infinite [Iterator] containing a value. +func Replicate[GU ~func() O.Option[P.Pair[GU, U]], U any](a U) GU { + return MakeBy[GU](F.Constant1[int](a)) +} + +// Repeat creates an [Iterator] containing a value repeated the specified number of times. +// Alias of [Replicate] combined with [Take] +func Repeat[GU ~func() O.Option[P.Pair[GU, U]], U any](n int, a U) GU { + return F.Pipe2( + a, + Replicate[GU], + Take[GU](n), + ) +} + +// Count creates an [Iterator] containing a consecutive sequence of integers starting with the provided start value +func Count[GU ~func() O.Option[P.Pair[GU, int]]](start int) GU { + return MakeBy[GU](N.Add(start)) +} + +func FilterMap[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) O.Option[V], U, V any](f FCT) func(ma GU) GV { + // pre-declare to avoid cyclic reference + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] + + recurse := func(ma GU) GV { + return F.Nullary2( + ma, + m, + ) + } + + m = O.Fold( + Empty[GV](), + func(t P.Pair[GU, U]) O.Option[P.Pair[GV, V]] { + r := recurse(Next(t)) + return O.MonadFold(f(Current(t)), r, F.Flow2( + F.Bind1st(P.MakePair[GV, V], r), + O.Some[P.Pair[GV, V]], + )) + }, + ) + + return recurse +} + +func Filter[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) bool, U any](f FCT) func(ma GU) GU { + return FilterMap[GU, GU](O.FromPredicate(f)) +} + +func Ap[GUV ~func() O.Option[P.Pair[GUV, func(U) V]], GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](ma GU) func(fab GUV) GV { + return Chain[GV, GUV](F.Bind1st(MonadMap[GV, GU], ma)) +} + +func MonadAp[GUV ~func() O.Option[P.Pair[GUV, func(U) V]], GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](fab GUV, ma GU) GV { + return Ap[GUV, GV, GU](ma)(fab) +} + +func FilterChain[GVV ~func() O.Option[P.Pair[GVV, GV]], GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) O.Option[GV], U, V any](f FCT) func(ma GU) GV { + return F.Flow2( + FilterMap[GVV, GU](f), + Flatten[GVV], + ) +} + +func FoldMap[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) V, U, V any](m M.Monoid[V]) func(FCT) func(ma GU) V { + return func(f FCT) func(ma GU) V { + return Reduce[GU](func(cur V, a U) V { + return m.Concat(cur, f(a)) + }, m.Empty()) + } +} + +func Fold[GU ~func() O.Option[P.Pair[GU, U]], U any](m M.Monoid[U]) func(ma GU) U { + return Reduce[GU](m.Concat, m.Empty()) +} diff --git a/v2/iterator/stateless/generic/last.go b/v2/iterator/stateless/generic/last.go new file mode 100644 index 0000000..49f5c65 --- /dev/null +++ b/v2/iterator/stateless/generic/last.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// Last returns the last item in an iterator if such an item exists +func Last[GU ~func() O.Option[P.Pair[GU, U]], U any](mu GU) O.Option[U] { + return reduce(mu, F.Ignore1of2[O.Option[U]](O.Of[U]), O.None[U]()) +} diff --git a/v2/iterator/stateless/generic/monad.go b/v2/iterator/stateless/generic/monad.go new file mode 100644 index 0000000..80ca114 --- /dev/null +++ b/v2/iterator/stateless/generic/monad.go @@ -0,0 +1,45 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/monad" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +type iteratorMonad[A, B any, GA ~func() O.Option[P.Pair[GA, A]], GB ~func() O.Option[P.Pair[GB, B]], GAB ~func() O.Option[P.Pair[GAB, func(A) B]]] struct{} + +func (o *iteratorMonad[A, B, GA, GB, GAB]) Of(a A) GA { + return Of[GA, A](a) +} + +func (o *iteratorMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB { + return Map[GB, GA, func(A) B, A, B](f) +} + +func (o *iteratorMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB { + return Chain[GB, GA, A, B](f) +} + +func (o *iteratorMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB { + return Ap[GAB, GB, GA, A, B](fa) +} + +// Monad implements the monadic operations for iterators +func Monad[A, B any, GA ~func() O.Option[P.Pair[GA, A]], GB ~func() O.Option[P.Pair[GB, B]], GAB ~func() O.Option[P.Pair[GAB, func(A) B]]]() monad.Monad[A, B, GA, GB, GAB] { + return &iteratorMonad[A, B, GA, GB, GAB]{} +} diff --git a/v2/iterator/stateless/generic/monoid.go b/v2/iterator/stateless/generic/monoid.go new file mode 100644 index 0000000..2fac39c --- /dev/null +++ b/v2/iterator/stateless/generic/monoid.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +func Monoid[GU ~func() O.Option[P.Pair[GU, U]], U any]() M.Monoid[GU] { + return M.MakeMonoid( + F.Swap(concat[GU]), + Empty[GU](), + ) +} diff --git a/v2/iterator/stateless/generic/reflect.go b/v2/iterator/stateless/generic/reflect.go new file mode 100644 index 0000000..ebdf9e5 --- /dev/null +++ b/v2/iterator/stateless/generic/reflect.go @@ -0,0 +1,53 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + R "reflect" + + F "github.com/IBM/fp-go/v2/function" + LG "github.com/IBM/fp-go/v2/io/generic" + L "github.com/IBM/fp-go/v2/lazy" + N "github.com/IBM/fp-go/v2/number" + I "github.com/IBM/fp-go/v2/number/integer" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +func FromReflect[GR ~func() O.Option[P.Pair[GR, R.Value]]](val R.Value) GR { + // recursive callback + var recurse func(idx int) GR + + // limits the index + fromPred := O.FromPredicate(I.Between(0, val.Len())) + + recurse = func(idx int) GR { + return F.Pipe3( + idx, + L.Of[int], + L.Map(fromPred), + LG.Map[L.Lazy[O.Option[int]], GR](O.Map( + F.Flow2( + P.Of[int], + P.BiMap(F.Flow2(N.Add(1), recurse), val.Index), + ), + )), + ) + } + + // start the recursion + return recurse(0) +} diff --git a/v2/iterator/stateless/generic/scan.go b/v2/iterator/stateless/generic/scan.go new file mode 100644 index 0000000..bc3ecd5 --- /dev/null +++ b/v2/iterator/stateless/generic/scan.go @@ -0,0 +1,45 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +func apTuple[A, B any](t P.Pair[func(A) B, A]) P.Pair[B, A] { + return P.MakePair(P.Head(t)(P.Tail(t)), P.Tail(t)) +} + +func Scan[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(V, U) V, U, V any](f FCT, initial V) func(ma GU) GV { + // pre-declare to avoid cyclic reference + var m func(GU) func(V) GV + + recurse := func(ma GU, current V) GV { + return F.Nullary2( + ma, + O.Map(F.Flow2( + P.BiMap(m, F.Bind1st(f, current)), + apTuple[V, GV], + )), + ) + } + + m = F.Curry2(recurse) + + return F.Bind2nd(recurse, initial) +} diff --git a/v2/iterator/stateless/generic/take.go b/v2/iterator/stateless/generic/take.go new file mode 100644 index 0000000..74634a3 --- /dev/null +++ b/v2/iterator/stateless/generic/take.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number/integer" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +func Take[GU ~func() O.Option[P.Pair[GU, U]], U any](n int) func(ma GU) GU { + // pre-declare to avoid cyclic reference + var recurse func(ma GU, idx int) GU + + fromPred := O.FromPredicate(N.Between(0, n)) + + recurse = func(ma GU, idx int) GU { + return F.Nullary3( + F.Constant(idx), + fromPred, + O.Chain(F.Ignore1of1[int](F.Nullary2( + ma, + O.Map(P.BiMap(F.Bind2nd(recurse, idx+1), F.Identity[U])), + ))), + ) + } + + return F.Bind2nd(recurse, 0) +} diff --git a/v2/iterator/stateless/generic/uniq.go b/v2/iterator/stateless/generic/uniq.go new file mode 100644 index 0000000..78fccb4 --- /dev/null +++ b/v2/iterator/stateless/generic/uniq.go @@ -0,0 +1,62 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// addToMap makes a deep copy of a map and adds a value +func addToMap[A comparable](a A, m map[A]bool) map[A]bool { + cpy := make(map[A]bool, len(m)+1) + for k, v := range m { + cpy[k] = v + } + cpy[a] = true + return cpy +} + +func Uniq[AS ~func() O.Option[P.Pair[AS, A]], K comparable, A any](f func(A) K) func(as AS) AS { + + var recurse func(as AS, mp map[K]bool) AS + + recurse = func(as AS, mp map[K]bool) AS { + return F.Nullary2( + as, + O.Chain(func(a P.Pair[AS, A]) O.Option[P.Pair[AS, A]] { + return F.Pipe3( + P.Tail(a), + f, + O.FromPredicate(func(k K) bool { + _, ok := mp[k] + return !ok + }), + O.Fold(recurse(P.Head(a), mp), func(k K) O.Option[P.Pair[AS, A]] { + return O.Of(P.MakePair(recurse(P.Head(a), addToMap(k, mp)), P.Tail(a))) + }), + ) + }), + ) + } + + return F.Bind2nd(recurse, make(map[K]bool, 0)) +} + +func StrictUniq[AS ~func() O.Option[P.Pair[AS, A]], A comparable](as AS) AS { + return Uniq[AS](F.Identity[A])(as) +} diff --git a/v2/iterator/stateless/generic/zip.go b/v2/iterator/stateless/generic/zip.go new file mode 100644 index 0000000..15364cb --- /dev/null +++ b/v2/iterator/stateless/generic/zip.go @@ -0,0 +1,54 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// ZipWith applies a function to pairs of elements at the same index in two iterators, collecting the results in a new iterator. If one +// input iterator is short, excess elements of the longer iterator are discarded. +func ZipWith[AS ~func() O.Option[P.Pair[AS, A]], BS ~func() O.Option[P.Pair[BS, B]], CS ~func() O.Option[P.Pair[CS, C]], FCT ~func(A, B) C, A, B, C any](fa AS, fb BS, f FCT) CS { + // pre-declare to avoid cyclic reference + var m func(P.Pair[O.Option[P.Pair[AS, A]], O.Option[P.Pair[BS, B]]]) O.Option[P.Pair[CS, C]] + + recurse := func(as AS, bs BS) CS { + return func() O.Option[P.Pair[CS, C]] { + // combine + return F.Pipe1( + P.MakePair(as(), bs()), + m, + ) + } + } + + m = F.Flow2( + O.SequencePair[P.Pair[AS, A], P.Pair[BS, B]], + O.Map(func(t P.Pair[P.Pair[AS, A], P.Pair[BS, B]]) P.Pair[CS, C] { + return P.MakePair(recurse(P.Head(P.Head(t)), P.Head(P.Tail(t))), f(P.Tail(P.Head(t)), P.Tail(P.Tail(t)))) + })) + + // trigger the recursion + return recurse(fa, fb) +} + +// Zip takes two iterators and returns an iterators of corresponding pairs. If one input iterators is short, excess elements of the +// longer iterator are discarded +func Zip[AS ~func() O.Option[P.Pair[AS, A]], BS ~func() O.Option[P.Pair[BS, B]], CS ~func() O.Option[P.Pair[CS, P.Pair[A, B]]], A, B any](fb BS) func(AS) CS { + return F.Bind23of3(ZipWith[AS, BS, CS, func(A, B) P.Pair[A, B]])(fb, P.MakePair[A, B]) +} diff --git a/v2/iterator/stateless/io.go b/v2/iterator/stateless/io.go new file mode 100644 index 0000000..161166f --- /dev/null +++ b/v2/iterator/stateless/io.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + IO "github.com/IBM/fp-go/v2/io" + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" + L "github.com/IBM/fp-go/v2/lazy" +) + +// FromLazy returns an [Iterator] on top of a lazy function +func FromLazy[U any](l L.Lazy[U]) Iterator[U] { + return G.FromLazy[Iterator[U], L.Lazy[U]](l) +} + +// FromIO returns an [Iterator] on top of an IO function +func FromIO[U any](io IO.IO[U]) Iterator[U] { + return G.FromLazy[Iterator[U], IO.IO[U]](io) +} diff --git a/v2/iterator/stateless/io_test.go b/v2/iterator/stateless/io_test.go new file mode 100644 index 0000000..693d071 --- /dev/null +++ b/v2/iterator/stateless/io_test.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIteratorFromLazy(t *testing.T) { + num := rand.Int + + cit := FromLazy(num) + + // create arrays twice + c1 := ToArray(cit) + c2 := ToArray(cit) + + assert.Equal(t, 1, len(c1)) + assert.Equal(t, 1, len(c2)) + + assert.NotEqual(t, c1, c2) +} diff --git a/v2/iterator/stateless/iterator.go b/v2/iterator/stateless/iterator.go new file mode 100644 index 0000000..9c657cf --- /dev/null +++ b/v2/iterator/stateless/iterator.go @@ -0,0 +1,154 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" + L "github.com/IBM/fp-go/v2/lazy" + M "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// Iterator represents a stateless, pure way to iterate over a sequence +type Iterator[U any] L.Lazy[O.Option[P.Pair[Iterator[U], U]]] + +// Next returns the [Iterator] for the next element in an iterator `P.Pair` +func Next[U any](m P.Pair[Iterator[U], U]) Iterator[U] { + return G.Next(m) +} + +// Current returns the current element in an [Iterator] `P.Pair` +func Current[U any](m P.Pair[Iterator[U], U]) U { + return G.Current(m) +} + +// Empty returns the empty iterator +func Empty[U any]() Iterator[U] { + return G.Empty[Iterator[U]]() +} + +// Of returns an iterator with one single element +func Of[U any](a U) Iterator[U] { + return G.Of[Iterator[U]](a) +} + +// FromArray returns an iterator from multiple elements +func FromArray[U any](as []U) Iterator[U] { + return G.FromArray[Iterator[U]](as) +} + +// ToArray converts the iterator to an array +func ToArray[U any](u Iterator[U]) []U { + return G.ToArray[Iterator[U], []U](u) +} + +// Reduce applies a function for each value of the iterator with a floating result +func Reduce[U, V any](f func(V, U) V, initial V) func(Iterator[U]) V { + return G.Reduce[Iterator[U]](f, initial) +} + +// MonadMap transforms an [Iterator] of type [U] into an [Iterator] of type [V] via a mapping function +func MonadMap[U, V any](ma Iterator[U], f func(U) V) Iterator[V] { + return G.MonadMap[Iterator[V], Iterator[U]](ma, f) +} + +// Map transforms an [Iterator] of type [U] into an [Iterator] of type [V] via a mapping function +func Map[U, V any](f func(U) V) func(ma Iterator[U]) Iterator[V] { + return G.Map[Iterator[V], Iterator[U]](f) +} + +func MonadChain[U, V any](ma Iterator[U], f func(U) Iterator[V]) Iterator[V] { + return G.MonadChain[Iterator[V], Iterator[U]](ma, f) +} + +func Chain[U, V any](f func(U) Iterator[V]) func(Iterator[U]) Iterator[V] { + return G.Chain[Iterator[V], Iterator[U]](f) +} + +// Flatten converts an [Iterator] of [Iterator] into a simple [Iterator] +func Flatten[U any](ma Iterator[Iterator[U]]) Iterator[U] { + return G.Flatten[Iterator[Iterator[U]], Iterator[U]](ma) +} + +// From constructs an [Iterator] from a set of variadic arguments +func From[U any](data ...U) Iterator[U] { + return G.From[Iterator[U]](data...) +} + +// MakeBy returns an [Iterator] with an infinite number of elements initialized with `f(i)` +func MakeBy[FCT ~func(int) U, U any](f FCT) Iterator[U] { + return G.MakeBy[Iterator[U]](f) +} + +// Replicate creates an [Iterator] containing a value repeated an infinite number of times. +func Replicate[U any](a U) Iterator[U] { + return G.Replicate[Iterator[U]](a) +} + +// FilterMap filters and transforms the content of an iterator +func FilterMap[U, V any](f func(U) O.Option[V]) func(ma Iterator[U]) Iterator[V] { + return G.FilterMap[Iterator[V], Iterator[U]](f) +} + +// Filter filters the content of an iterator +func Filter[U any](f func(U) bool) func(ma Iterator[U]) Iterator[U] { + return G.Filter[Iterator[U]](f) +} + +// Ap is the applicative functor for iterators +func Ap[V, U any](ma Iterator[U]) func(Iterator[func(U) V]) Iterator[V] { + return G.Ap[Iterator[func(U) V], Iterator[V]](ma) +} + +// MonadAp is the applicative functor for iterators +func MonadAp[V, U any](fab Iterator[func(U) V], ma Iterator[U]) Iterator[V] { + return G.MonadAp[Iterator[func(U) V], Iterator[V]](fab, ma) +} + +// Repeat creates an [Iterator] containing a value repeated the specified number of times. +// Alias of [Replicate] +func Repeat[U any](n int, a U) Iterator[U] { + return G.Repeat[Iterator[U]](n, a) +} + +// Count creates an [Iterator] containing a consecutive sequence of integers starting with the provided start value +func Count(start int) Iterator[int] { + return G.Count[Iterator[int]](start) +} + +// FilterChain filters and transforms the content of an iterator +func FilterChain[U, V any](f func(U) O.Option[Iterator[V]]) func(ma Iterator[U]) Iterator[V] { + return G.FilterChain[Iterator[Iterator[V]], Iterator[V], Iterator[U]](f) +} + +// FoldMap maps and folds an iterator. Map the iterator passing each value to the iterating function. Then fold the results using the provided Monoid. +func FoldMap[U, V any](m M.Monoid[V]) func(func(U) V) func(ma Iterator[U]) V { + return G.FoldMap[Iterator[U], func(U) V, U, V](m) +} + +// Fold folds the iterator using the provided Monoid. +func Fold[U any](m M.Monoid[U]) func(Iterator[U]) U { + return G.Fold[Iterator[U]](m) +} + +func MonadChainFirst[U, V any](ma Iterator[U], f func(U) Iterator[V]) Iterator[U] { + return G.MonadChainFirst[Iterator[V], Iterator[U], U, V](ma, f) +} + +func ChainFirst[U, V any](f func(U) Iterator[V]) func(Iterator[U]) Iterator[U] { + return G.ChainFirst[Iterator[V], Iterator[U], U, V](f) +} diff --git a/v2/iterator/stateless/iterator_test.go b/v2/iterator/stateless/iterator_test.go new file mode 100644 index 0000000..0f0064e --- /dev/null +++ b/v2/iterator/stateless/iterator_test.go @@ -0,0 +1,117 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "fmt" + "math" + "strings" + "testing" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + O "github.com/IBM/fp-go/v2/option" + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +func TestIterator(t *testing.T) { + + result := F.Pipe2( + A.From(1, 2, 3), + FromArray[int], + Reduce(utils.Sum, 0), + ) + + assert.Equal(t, 6, result) +} + +func TestChain(t *testing.T) { + + outer := From(1, 2, 3) + + inner := func(data int) Iterator[string] { + return F.Pipe2( + A.From(0, 1), + FromArray[int], + Map(func(idx int) string { + return fmt.Sprintf("item[%d][%d]", data, idx) + }), + ) + } + + total := F.Pipe2( + outer, + Chain(inner), + ToArray[string], + ) + + assert.Equal(t, A.From("item[1][0]", "item[1][1]", "item[2][0]", "item[2][1]", "item[3][0]", "item[3][1]"), total) +} + +func isPrimeNumber(num int) bool { + if num <= 2 { + return true + } + sqRoot := int(math.Sqrt(float64(num))) + for i := 2; i <= sqRoot; i++ { + if num%i == 0 { + return false + } + } + return true +} + +func TestFilterMap(t *testing.T) { + + it := F.Pipe3( + MakeBy(utils.Inc), + Take[int](100), + FilterMap(O.FromPredicate(isPrimeNumber)), + ToArray[int], + ) + + assert.Equal(t, A.From(1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97), it) +} + +func TestAp(t *testing.T) { + + f := F.Curry3(func(s1 string, n int, s2 string) string { + return fmt.Sprintf("%s-%d-%s", s1, n, s2) + }) + + it := F.Pipe4( + Of(f), + Ap[func(int) func(string) string](From("a", "b")), + Ap[func(string) string](From(1, 2)), + Ap[string](From("c", "d")), + ToArray[string], + ) + + assert.Equal(t, A.From("a-1-c", "a-1-d", "a-2-c", "a-2-d", "b-1-c", "b-1-d", "b-2-c", "b-2-d"), it) +} + +func ExampleFoldMap() { + src := From("a", "b", "c") + + fold := FoldMap[string](S.Monoid)(strings.ToUpper) + + fmt.Println(fold(src)) + + // Output: ABC + +} diff --git a/v2/iterator/stateless/last.go b/v2/iterator/stateless/last.go new file mode 100644 index 0000000..bd845a3 --- /dev/null +++ b/v2/iterator/stateless/last.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// Last returns the last item in an iterator if such an item exists +// Note that the function will consume the [Iterator] in this call completely, to identify the last element. Do not use this for infinite iterators +func Last[U any](mu Iterator[U]) O.Option[U] { + return G.Last[Iterator[U]](mu) +} diff --git a/v2/iterator/stateless/last_test.go b/v2/iterator/stateless/last_test.go new file mode 100644 index 0000000..9b7866b --- /dev/null +++ b/v2/iterator/stateless/last_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +func TestLast(t *testing.T) { + + seq := From(1, 2, 3) + + fst := Last(seq) + + assert.Equal(t, O.Of(3), fst) +} + +func TestNoLast(t *testing.T) { + + seq := Empty[int]() + + fst := Last(seq) + + assert.Equal(t, O.None[int](), fst) +} diff --git a/v2/iterator/stateless/monad.go b/v2/iterator/stateless/monad.go new file mode 100644 index 0000000..9cb8a11 --- /dev/null +++ b/v2/iterator/stateless/monad.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "github.com/IBM/fp-go/v2/internal/monad" + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// Monad returns the monadic operations for an [Iterator] +func Monad[A, B any]() monad.Monad[A, B, Iterator[A], Iterator[B], Iterator[func(A) B]] { + return G.Monad[A, B, Iterator[A], Iterator[B], Iterator[func(A) B]]() +} diff --git a/v2/iterator/stateless/monoid.go b/v2/iterator/stateless/monoid.go new file mode 100644 index 0000000..815c4c4 --- /dev/null +++ b/v2/iterator/stateless/monoid.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" + M "github.com/IBM/fp-go/v2/monoid" +) + +// Monoid contructs a [M.Monoid] that concatenates two [Iterator]s +func Monoid[U any]() M.Monoid[Iterator[U]] { + return G.Monoid[Iterator[U]]() +} diff --git a/v2/iterator/stateless/reflect.go b/v2/iterator/stateless/reflect.go new file mode 100644 index 0000000..f414552 --- /dev/null +++ b/v2/iterator/stateless/reflect.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + R "reflect" + + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// FromReflect creates an iterator that can iterate over types that define [R.Index] and [R.Len] +func FromReflect(val R.Value) Iterator[R.Value] { + return G.FromReflect[Iterator[R.Value]](val) +} diff --git a/v2/iterator/stateless/reflect_test.go b/v2/iterator/stateless/reflect_test.go new file mode 100644 index 0000000..44e17d5 --- /dev/null +++ b/v2/iterator/stateless/reflect_test.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "reflect" + "testing" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestReflect(t *testing.T) { + ar := A.From("a", "b", "c") + + res := F.Pipe3( + reflect.ValueOf(ar), + FromReflect, + ToArray[reflect.Value], + A.Map(reflect.Value.String), + ) + + assert.Equal(t, ar, res) +} diff --git a/v2/iterator/stateless/scan.go b/v2/iterator/stateless/scan.go new file mode 100644 index 0000000..b972801 --- /dev/null +++ b/v2/iterator/stateless/scan.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// Scan takes an [Iterator] and returns a new [Iterator] of the same length, where the values +// of the new [Iterator] are the result of the application of `f` to the value of the +// source iterator with the previously accumulated value +func Scan[FCT ~func(V, U) V, U, V any](f FCT, initial V) func(ma Iterator[U]) Iterator[V] { + return G.Scan[Iterator[V], Iterator[U], FCT](f, initial) +} diff --git a/v2/iterator/stateless/scan_test.go b/v2/iterator/stateless/scan_test.go new file mode 100644 index 0000000..e5c17bf --- /dev/null +++ b/v2/iterator/stateless/scan_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + P "github.com/IBM/fp-go/v2/pair" + "github.com/stretchr/testify/assert" +) + +func TestScan(t *testing.T) { + + src := From("a", "b", "c") + + dst := F.Pipe1( + src, + Scan(func(cur P.Pair[int, string], val string) P.Pair[int, string] { + return P.MakePair(P.Head(cur)+1, val) + }, P.MakePair(0, "")), + ) + + assert.Equal(t, ToArray(From( + P.MakePair(1, "a"), + P.MakePair(2, "b"), + P.MakePair(3, "c"), + )), ToArray(dst)) +} diff --git a/v2/iterator/stateless/take.go b/v2/iterator/stateless/take.go new file mode 100644 index 0000000..8a5b2ab --- /dev/null +++ b/v2/iterator/stateless/take.go @@ -0,0 +1,25 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// Take limits the number of values in the [Iterator] to a maximum number +func Take[U any](n int) func(ma Iterator[U]) Iterator[U] { + return G.Take[Iterator[U]](n) +} diff --git a/v2/iterator/stateless/take_test.go b/v2/iterator/stateless/take_test.go new file mode 100644 index 0000000..9979d90 --- /dev/null +++ b/v2/iterator/stateless/take_test.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestTake(t *testing.T) { + + total := MakeBy(F.Identity[int]) + + trimmed := F.Pipe1( + total, + Take[int](10), + ) + + assert.Equal(t, A.MakeBy(10, F.Identity[int]), ToArray(trimmed)) + +} diff --git a/v2/iterator/stateless/uniq.go b/v2/iterator/stateless/uniq.go new file mode 100644 index 0000000..4fe3b60 --- /dev/null +++ b/v2/iterator/stateless/uniq.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" +) + +// StrictUniq converts an [Iterator] of arbitrary items into an [Iterator] or unique items +// where uniqueness is determined by the built-in uniqueness constraint +func StrictUniq[A comparable](as Iterator[A]) Iterator[A] { + return G.StrictUniq[Iterator[A]](as) +} + +// Uniq converts an [Iterator] of arbitrary items into an [Iterator] or unique items +// where uniqueness is determined based on a key extractor function +func Uniq[A any, K comparable](f func(A) K) func(as Iterator[A]) Iterator[A] { + return G.Uniq[Iterator[A], K](f) +} diff --git a/v2/iterator/stateless/uniq_test.go b/v2/iterator/stateless/uniq_test.go new file mode 100644 index 0000000..3507cd6 --- /dev/null +++ b/v2/iterator/stateless/uniq_test.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestUniq(t *testing.T) { + // iterator with duplicate items + dups := From(1, 2, 3, 1, 4, 5, 2) + + u := StrictUniq(dups) + + assert.Equal(t, ToArray(From(1, 2, 3, 4, 5)), ToArray(u)) +} diff --git a/v2/iterator/stateless/zip.go b/v2/iterator/stateless/zip.go new file mode 100644 index 0000000..a392950 --- /dev/null +++ b/v2/iterator/stateless/zip.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/v2/iterator/stateless/generic" + P "github.com/IBM/fp-go/v2/pair" +) + +// ZipWith applies a function to pairs of elements at the same index in two iterators, collecting the results in a new iterator. If one +// input iterator is short, excess elements of the longer iterator are discarded. +func ZipWith[FCT ~func(A, B) C, A, B, C any](fa Iterator[A], fb Iterator[B], f FCT) Iterator[C] { + return G.ZipWith[Iterator[A], Iterator[B], Iterator[C]](fa, fb, f) +} + +// Zip takes two iterators and returns an iterators of corresponding pairs. If one input iterators is short, excess elements of the +// longer iterator are discarded +func Zip[A, B any](fb Iterator[B]) func(Iterator[A]) Iterator[P.Pair[A, B]] { + return G.Zip[Iterator[A], Iterator[B], Iterator[P.Pair[A, B]]](fb) +} diff --git a/v2/iterator/stateless/zip_test.go b/v2/iterator/stateless/zip_test.go new file mode 100644 index 0000000..474ec83 --- /dev/null +++ b/v2/iterator/stateless/zip_test.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "fmt" + "testing" + + P "github.com/IBM/fp-go/v2/pair" + "github.com/stretchr/testify/assert" +) + +func TestZipWith(t *testing.T) { + left := From(1, 2, 3) + right := From("a", "b", "c", "d") + + res := ZipWith(left, right, func(l int, r string) string { + return fmt.Sprintf("%s%d", r, l) + }) + + assert.Equal(t, ToArray(From("a1", "b2", "c3")), ToArray(res)) +} + +func TestZip(t *testing.T) { + left := From(1, 2, 3) + right := From("a", "b", "c", "d") + + res := Zip[string](left)(right) + + assert.Equal(t, ToArray(From(P.MakePair("a", 1), P.MakePair("b", 2), P.MakePair("c", 3))), ToArray(res)) +} diff --git a/v2/json/json.go b/v2/json/json.go new file mode 100644 index 0000000..67fd58c --- /dev/null +++ b/v2/json/json.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package json + +import ( + "encoding/json" + + E "github.com/IBM/fp-go/v2/either" +) + +// Unmarshal parses a JSON data structure from bytes +func Unmarshal[A any](data []byte) E.Either[error, A] { + var result A + err := json.Unmarshal(data, &result) + return E.TryCatchError(result, err) +} + +// Marshal converts a data structure to json +func Marshal[A any](a A) E.Either[error, []byte] { + return E.TryCatchError(json.Marshal(a)) +} diff --git a/v2/json/json_test.go b/v2/json/json_test.go new file mode 100644 index 0000000..467d915 --- /dev/null +++ b/v2/json/json_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package json + +import ( + "fmt" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +type Json map[string]any + +func TestJsonMarshal(t *testing.T) { + + resRight := Unmarshal[Json]([]byte("{\"a\": \"b\"}")) + assert.True(t, E.IsRight(resRight)) + + resLeft := Unmarshal[Json]([]byte("{\"a\"")) + assert.True(t, E.IsLeft(resLeft)) + + res1 := F.Pipe1( + resRight, + E.Chain(Marshal[Json]), + ) + fmt.Println(res1) +} diff --git a/v2/json/type.go b/v2/json/type.go new file mode 100644 index 0000000..5d8b941 --- /dev/null +++ b/v2/json/type.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package json + +import ( + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +func ToTypeE[A any](src any) E.Either[error, A] { + return F.Pipe2( + src, + Marshal[any], + E.Chain(Unmarshal[A]), + ) +} + +func ToTypeO[A any](src any) O.Option[A] { + return F.Pipe1( + ToTypeE[A](src), + E.ToOption[error, A], + ) +} diff --git a/v2/json/type_test.go b/v2/json/type_test.go new file mode 100644 index 0000000..4c741a8 --- /dev/null +++ b/v2/json/type_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package json + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +type TestType struct { + A string `json:"a"` + B int `json:"b"` +} + +func TestToType(t *testing.T) { + + generic := map[string]any{"a": "value", "b": 1} + + assert.True(t, E.IsRight(ToTypeE[TestType](generic))) + assert.True(t, E.IsRight(ToTypeE[TestType](&generic))) + + assert.Equal(t, E.Right[error](&TestType{A: "value", B: 1}), ToTypeE[*TestType](&generic)) + assert.Equal(t, E.Right[error](TestType{A: "value", B: 1}), F.Pipe1(ToTypeE[*TestType](&generic), E.Map[error](F.Deref[TestType]))) +} diff --git a/v2/lambda/y.go b/v2/lambda/y.go new file mode 100644 index 0000000..60cc176 --- /dev/null +++ b/v2/lambda/y.go @@ -0,0 +1,29 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lambda + +// Y is the Y-combinator based on https://dreamsongs.com/Files/WhyOfY.pdf +func Y[Endo ~func(RecFct) RecFct, RecFct ~func(T) R, T, R any](f Endo) RecFct { + + type internal[RecFct ~func(T) R, T, R any] func(internal[RecFct, T, R]) RecFct + + g := func(h internal[RecFct, T, R]) RecFct { + return func(t T) R { + return f(h(h))(t) + } + } + return g(g) +} diff --git a/v2/lambda/y_test.go b/v2/lambda/y_test.go new file mode 100644 index 0000000..bd9d7fa --- /dev/null +++ b/v2/lambda/y_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lambda + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFactorial(t *testing.T) { + fct := Y(func(r func(int) int) func(int) int { + return func(n int) int { + if n <= 0 { + return 1 + } + return n * r(n-1) + } + }) + assert.Equal(t, 3628800, fct(10)) +} diff --git a/v2/lazy/apply.go b/v2/lazy/apply.go new file mode 100644 index 0000000..5b3040d --- /dev/null +++ b/v2/lazy/apply.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + G "github.com/IBM/fp-go/v2/io/generic" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[Lazy[A]] { + return G.ApplySemigroup[Lazy[A]](s) +} + +func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Lazy[A]] { + return G.ApplicativeMonoid[Lazy[A]](m) +} diff --git a/v2/lazy/bind.go b/v2/lazy/bind.go new file mode 100644 index 0000000..7d2e40a --- /dev/null +++ b/v2/lazy/bind.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) Lazy[S] { + return G.Do[Lazy[S], S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) Lazy[T], +) func(Lazy[S1]) Lazy[S2] { + return G.Bind[Lazy[S1], Lazy[S2], Lazy[T], S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(Lazy[S1]) Lazy[S2] { + return G.Let[Lazy[S1], Lazy[S2], S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(Lazy[S1]) Lazy[S2] { + return G.LetTo[Lazy[S1], Lazy[S2], S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func(Lazy[T]) Lazy[S1] { + return G.BindTo[Lazy[S1], Lazy[T], S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa Lazy[T], +) func(Lazy[S1]) Lazy[S2] { + return G.ApS[Lazy[S1], Lazy[S2], Lazy[T], S1, S2, T](setter, fa) +} diff --git a/v2/lazy/bind_test.go b/v2/lazy/bind_test.go new file mode 100644 index 0000000..efbfe73 --- /dev/null +++ b/v2/lazy/bind_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) Lazy[string] { + return Of("Doe") +} + +func getGivenName(s utils.WithLastName) Lazy[string] { + return Of("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(), "John Doe") +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + ApS(utils.SetLastName, Of("Doe")), + ApS(utils.SetGivenName, Of("John")), + Map(utils.GetFullName), + ) + + assert.Equal(t, res(), "John Doe") +} diff --git a/v2/lazy/eq.go b/v2/lazy/eq.go new file mode 100644 index 0000000..4974e68 --- /dev/null +++ b/v2/lazy/eq.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Eq implements the equals predicate for values contained in the IO monad +func Eq[A any](e EQ.Eq[A]) EQ.Eq[Lazy[A]] { + return G.Eq[Lazy[A]](e) +} diff --git a/v2/lazy/example_lazy_test.go b/v2/lazy/example_lazy_test.go new file mode 100644 index 0000000..cff5d03 --- /dev/null +++ b/v2/lazy/example_lazy_test.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + "fmt" + "strconv" + + F "github.com/IBM/fp-go/v2/function" +) + +func ExampleLazy_creation() { + // lazy function of a constant value + val := Of(42) + // create another function to transform this + valS := F.Pipe1( + val, + Map(strconv.Itoa), + ) + + fmt.Println(valS()) + + // Output: + // 42 +} diff --git a/v2/lazy/lazy.go b/v2/lazy/lazy.go new file mode 100644 index 0000000..dab660f --- /dev/null +++ b/v2/lazy/lazy.go @@ -0,0 +1,139 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + "time" + + G "github.com/IBM/fp-go/v2/io/generic" +) + +// Lazy represents a synchronous computation without side effects +type Lazy[A any] func() A + +func MakeLazy[A any](f func() A) Lazy[A] { + return G.MakeIO[Lazy[A]](f) +} + +func Of[A any](a A) Lazy[A] { + return G.Of[Lazy[A]](a) +} + +func FromLazy[A any](a Lazy[A]) Lazy[A] { + return G.FromIO(a) +} + +// FromImpure converts a side effect without a return value into a side effect that returns any +func FromImpure(f func()) Lazy[any] { + return G.FromImpure[Lazy[any]](f) +} + +func MonadOf[A any](a A) Lazy[A] { + return G.MonadOf[Lazy[A]](a) +} + +func MonadMap[A, B any](fa Lazy[A], f func(A) B) Lazy[B] { + return G.MonadMap[Lazy[A], Lazy[B]](fa, f) +} + +func Map[A, B any](f func(A) B) func(fa Lazy[A]) Lazy[B] { + return G.Map[Lazy[A], Lazy[B]](f) +} + +func MonadMapTo[A, B any](fa Lazy[A], b B) Lazy[B] { + return G.MonadMapTo[Lazy[A], Lazy[B]](fa, b) +} + +func MapTo[A, B any](b B) func(Lazy[A]) Lazy[B] { + return G.MapTo[Lazy[A], Lazy[B]](b) +} + +// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation. +func MonadChain[A, B any](fa Lazy[A], f func(A) Lazy[B]) Lazy[B] { + return G.MonadChain(fa, f) +} + +// Chain composes computations in sequence, using the return value of one computation to determine the next computation. +func Chain[A, B any](f func(A) Lazy[B]) func(Lazy[A]) Lazy[B] { + return G.Chain[Lazy[A]](f) +} + +func MonadAp[B, A any](mab Lazy[func(A) B], ma Lazy[A]) Lazy[B] { + return G.MonadApSeq[Lazy[A], Lazy[B]](mab, ma) +} + +func Ap[B, A any](ma Lazy[A]) func(Lazy[func(A) B]) Lazy[B] { + return G.ApSeq[Lazy[B], Lazy[func(A) B], Lazy[A]](ma) +} + +func Flatten[A any](mma Lazy[Lazy[A]]) Lazy[A] { + return G.Flatten(mma) +} + +// Memoize computes the value of the provided [Lazy] monad lazily but exactly once +func Memoize[A any](ma Lazy[A]) Lazy[A] { + return G.Memoize(ma) +} + +// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and +// keeping only the result of the first. +func MonadChainFirst[A, B any](fa Lazy[A], f func(A) Lazy[B]) Lazy[A] { + return G.MonadChainFirst(fa, f) +} + +// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and +// keeping only the result of the first. +func ChainFirst[A, B any](f func(A) Lazy[B]) func(Lazy[A]) Lazy[A] { + return G.ChainFirst[Lazy[A]](f) +} + +// MonadApFirst combines two effectful actions, keeping only the result of the first. +func MonadApFirst[A, B any](first Lazy[A], second Lazy[B]) Lazy[A] { + return G.MonadApFirst[Lazy[A], Lazy[B], Lazy[func(B) A]](first, second) +} + +// ApFirst combines two effectful actions, keeping only the result of the first. +func ApFirst[A, B any](second Lazy[B]) func(Lazy[A]) Lazy[A] { + return G.ApFirst[Lazy[A], Lazy[B], Lazy[func(B) A]](second) +} + +// MonadApSecond combines two effectful actions, keeping only the result of the second. +func MonadApSecond[A, B any](first Lazy[A], second Lazy[B]) Lazy[B] { + return G.MonadApSecond[Lazy[A], Lazy[B], Lazy[func(B) B]](first, second) +} + +// ApSecond combines two effectful actions, keeping only the result of the second. +func ApSecond[A, B any](second Lazy[B]) func(Lazy[A]) Lazy[B] { + return G.ApSecond[Lazy[A], Lazy[B], Lazy[func(B) B]](second) +} + +// MonadChainTo composes computations in sequence, ignoring the return value of the first computation +func MonadChainTo[A, B any](fa Lazy[A], fb Lazy[B]) Lazy[B] { + return G.MonadChainTo(fa, fb) +} + +// ChainTo composes computations in sequence, ignoring the return value of the first computation +func ChainTo[A, B any](fb Lazy[B]) func(Lazy[A]) Lazy[B] { + return G.ChainTo[Lazy[A]](fb) +} + +// Now returns the current timestamp +var Now = G.Now[Lazy[time.Time]]() + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[A any](gen func() Lazy[A]) Lazy[A] { + return G.Defer[Lazy[A]](gen) +} diff --git a/v2/lazy/lazy_test.go b/v2/lazy/lazy_test.go new file mode 100644 index 0000000..7dd7e66 --- /dev/null +++ b/v2/lazy/lazy_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + "math/rand" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func TestMap(t *testing.T) { + assert.Equal(t, 2, F.Pipe1(Of(1), Map(utils.Double))()) +} + +func TestChain(t *testing.T) { + f := func(n int) Lazy[int] { + return Of(n * 2) + } + assert.Equal(t, 2, F.Pipe1(Of(1), Chain(f))()) +} + +func TestAp(t *testing.T) { + assert.Equal(t, 2, F.Pipe1(Of(utils.Double), Ap[int, int](Of(1)))()) +} + +func TestFlatten(t *testing.T) { + assert.Equal(t, 1, F.Pipe1(Of(Of(1)), Flatten[int])()) +} + +func TestMemoize(t *testing.T) { + data := Memoize(MakeLazy(rand.Int)) + + value1 := data() + value2 := data() + + assert.Equal(t, value1, value2) +} + +func TestApFirst(t *testing.T) { + + x := F.Pipe1( + Of("a"), + ApFirst[string](Of("b")), + ) + + assert.Equal(t, "a", x()) +} + +func TestApSecond(t *testing.T) { + + x := F.Pipe1( + Of("a"), + ApSecond[string](Of("b")), + ) + + assert.Equal(t, "b", x()) +} diff --git a/v2/lazy/retry.go b/v2/lazy/retry.go new file mode 100644 index 0000000..fc327f1 --- /dev/null +++ b/v2/lazy/retry.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + G "github.com/IBM/fp-go/v2/io/generic" + R "github.com/IBM/fp-go/v2/retry" +) + +// Retrying will retry the actions according to the check policy +// +// policy - refers to the retry policy +// action - converts a status into an operation to be executed +// check - checks if the result of the action needs to be retried +func Retrying[A any]( + policy R.RetryPolicy, + action func(R.RetryStatus) Lazy[A], + check func(A) bool, +) Lazy[A] { + return G.Retrying(policy, action, check) +} diff --git a/v2/lazy/retry_test.go b/v2/lazy/retry_test.go new file mode 100644 index 0000000..ece86c0 --- /dev/null +++ b/v2/lazy/retry_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + "fmt" + "strings" + "testing" + "time" + + R "github.com/IBM/fp-go/v2/retry" + "github.com/stretchr/testify/assert" +) + +var expLogBackoff = R.ExponentialBackoff(10) + +// our retry policy with a 1s cap +var testLogPolicy = R.CapDelay( + 2*time.Second, + R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)), +) + +func TestRetry(t *testing.T) { + action := func(status R.RetryStatus) Lazy[string] { + return Of(fmt.Sprintf("Retrying %d", status.IterNumber)) + } + check := func(value string) bool { + return !strings.Contains(value, "5") + } + + r := Retrying(testLogPolicy, action, check) + + assert.Equal(t, "Retrying 5", r()) +} diff --git a/v2/lazy/sequence.go b/v2/lazy/sequence.go new file mode 100644 index 0000000..dfa3458 --- /dev/null +++ b/v2/lazy/sequence.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + G "github.com/IBM/fp-go/v2/io/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[A any](a Lazy[A]) Lazy[T.Tuple1[A]] { + return G.SequenceT1[Lazy[T.Tuple1[A]]](a) +} + +func SequenceT2[A, B any](a Lazy[A], b Lazy[B]) Lazy[T.Tuple2[A, B]] { + return G.SequenceT2[Lazy[T.Tuple2[A, B]]](a, b) +} + +func SequenceT3[A, B, C any](a Lazy[A], b Lazy[B], c Lazy[C]) Lazy[T.Tuple3[A, B, C]] { + return G.SequenceT3[Lazy[T.Tuple3[A, B, C]]](a, b, c) +} + +func SequenceT4[A, B, C, D any](a Lazy[A], b Lazy[B], c Lazy[C], d Lazy[D]) Lazy[T.Tuple4[A, B, C, D]] { + return G.SequenceT4[Lazy[T.Tuple4[A, B, C, D]]](a, b, c, d) +} diff --git a/v2/lazy/testing/laws.go b/v2/lazy/testing/laws.go new file mode 100644 index 0000000..2ff2783 --- /dev/null +++ b/v2/lazy/testing/laws.go @@ -0,0 +1,74 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" + "github.com/IBM/fp-go/v2/lazy" +) + +// AssertLaws asserts the apply monad laws for the `Either` monad +func AssertLaws[A, B, C any](t *testing.T, + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + return L.AssertLaws(t, + lazy.Eq(eqa), + lazy.Eq(eqb), + lazy.Eq(eqc), + + lazy.Of[A], + lazy.Of[B], + lazy.Of[C], + + lazy.Of[func(A) A], + lazy.Of[func(A) B], + lazy.Of[func(B) C], + lazy.Of[func(func(A) B) B], + + lazy.MonadMap[A, A], + lazy.MonadMap[A, B], + lazy.MonadMap[A, C], + lazy.MonadMap[B, C], + + lazy.MonadMap[func(B) C, func(func(A) B) func(A) C], + + lazy.MonadChain[A, A], + lazy.MonadChain[A, B], + lazy.MonadChain[A, C], + lazy.MonadChain[B, C], + + lazy.MonadAp[A, A], + lazy.MonadAp[B, A], + lazy.MonadAp[C, B], + lazy.MonadAp[C, A], + + lazy.MonadAp[B, func(A) B], + lazy.MonadAp[func(A) C, func(A) B], + + ab, + bc, + ) + +} diff --git a/v2/lazy/testing/laws_test.go b/v2/lazy/testing/laws_test.go new file mode 100644 index 0000000..43c0f37 --- /dev/null +++ b/v2/lazy/testing/laws_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqa, eqb, eqc, ab, bc) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/lazy/traverse.go b/v2/lazy/traverse.go new file mode 100644 index 0000000..edc127b --- /dev/null +++ b/v2/lazy/traverse.go @@ -0,0 +1,62 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lazy + +import ( + G "github.com/IBM/fp-go/v2/io/generic" +) + +func MonadTraverseArray[A, B any](tas []A, f func(A) Lazy[B]) Lazy[[]B] { + return G.MonadTraverseArray[Lazy[B], Lazy[[]B]](tas, f) +} + +// TraverseArray applies a function returning an [IO] to all elements in an array and the +// transforms this into an [IO] of that array +func TraverseArray[A, B any](f func(A) Lazy[B]) func([]A) Lazy[[]B] { + return G.TraverseArray[Lazy[B], Lazy[[]B], []A](f) +} + +// TraverseArrayWithIndex applies a function returning an [IO] to all elements in an array and the +// transforms this into an [IO] of that array +func TraverseArrayWithIndex[A, B any](f func(int, A) Lazy[B]) func([]A) Lazy[[]B] { + return G.TraverseArrayWithIndex[Lazy[B], Lazy[[]B], []A](f) +} + +// SequenceArray converts an array of [IO] to an [IO] of an array +func SequenceArray[A any](tas []Lazy[A]) Lazy[[]A] { + return G.SequenceArray[Lazy[A], Lazy[[]A]](tas) +} + +func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) Lazy[B]) Lazy[map[K]B] { + return G.MonadTraverseRecord[Lazy[map[K]B]](tas, f) +} + +// TraverseRecord applies a function returning an [IO] to all elements in a record and the +// transforms this into an [IO] of that record +func TraverseRecord[K comparable, A, B any](f func(A) Lazy[B]) func(map[K]A) Lazy[map[K]B] { + return G.TraverseRecord[Lazy[map[K]B], map[K]A, Lazy[B]](f) +} + +// TraverseRecord applies a function returning an [IO] to all elements in a record and the +// transforms this into an [IO] of that record +func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) Lazy[B]) func(map[K]A) Lazy[map[K]B] { + return G.TraverseRecordWithIndex[Lazy[B], Lazy[map[K]B], map[K]A](f) +} + +// SequenceRecord converts a record of [IO] to an [IO] of a record +func SequenceRecord[K comparable, A any](tas map[K]Lazy[A]) Lazy[map[K]A] { + return G.SequenceRecord[Lazy[A], Lazy[map[K]A]](tas) +} diff --git a/v2/logging/logger.go b/v2/logging/logger.go new file mode 100644 index 0000000..e855022 --- /dev/null +++ b/v2/logging/logger.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logging + +import ( + "log" +) + +func LoggingCallbacks(loggers ...*log.Logger) (func(string, ...any), func(string, ...any)) { + switch len(loggers) { + case 0: + def := log.Default() + return def.Printf, def.Printf + case 1: + log0 := loggers[0] + return log0.Printf, log0.Printf + default: + return loggers[0].Printf, loggers[1].Printf + } +} diff --git a/v2/magma/array.go b/v2/magma/array.go new file mode 100644 index 0000000..c79ea42 --- /dev/null +++ b/v2/magma/array.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package magma + +import ( + F "github.com/IBM/fp-go/v2/function" + AR "github.com/IBM/fp-go/v2/internal/array" +) + +func GenericMonadConcatAll[GA ~[]A, A any](m Magma[A]) func(GA, A) A { + return func(as GA, first A) A { + return AR.Reduce(as, m.Concat, first) + } +} + +// GenericConcatAll concats all items using the semigroup and a starting value +func GenericConcatAll[GA ~[]A, A any](m Magma[A]) func(A) func(GA) A { + ca := GenericMonadConcatAll[GA](m) + return func(a A) func(GA) A { + return F.Bind2nd(ca, a) + } +} + +func MonadConcatAll[A any](m Magma[A]) func([]A, A) A { + return GenericMonadConcatAll[[]A](m) +} + +// ConcatAll concats all items using the semigroup and a starting value +func ConcatAll[A any](m Magma[A]) func(A) func([]A) A { + return GenericConcatAll[[]A](m) +} diff --git a/v2/magma/magma.go b/v2/magma/magma.go new file mode 100644 index 0000000..19f261c --- /dev/null +++ b/v2/magma/magma.go @@ -0,0 +1,99 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package magma + +type Magma[A any] interface { + Concat(x A, y A) A +} + +type magma[A any] struct { + c func(A, A) A +} + +func (m magma[A]) Concat(x A, y A) A { + return m.c(x, y) +} + +func MakeMagma[A any](c func(A, A) A) Magma[A] { + return magma[A]{c: c} +} + +func Reverse[A any](m Magma[A]) Magma[A] { + return MakeMagma(func(x A, y A) A { + return m.Concat(y, x) + }) +} + +func filterFirst[A any](p func(A) bool, c func(A, A) A, x A, y A) A { + if p(x) { + return c(x, y) + } + return y +} + +func filterSecond[A any](p func(A) bool, c func(A, A) A, x A, y A) A { + if p(y) { + return c(x, y) + } + return x +} + +func FilterFirst[A any](p func(A) bool) func(Magma[A]) Magma[A] { + return func(m Magma[A]) Magma[A] { + c := m.Concat + return MakeMagma(func(x A, y A) A { + return filterFirst(p, c, x, y) + }) + } +} + +func FilterSecond[A any](p func(A) bool) func(Magma[A]) Magma[A] { + return func(m Magma[A]) Magma[A] { + c := m.Concat + return MakeMagma(func(x, y A) A { + return filterSecond(p, c, x, y) + }) + } +} + +func first[A any](x, _ A) A { + return x +} + +func second[A any](_, y A) A { + return y +} + +func First[A any]() Magma[A] { + return MakeMagma(first[A]) +} + +func Second[A any]() Magma[A] { + return MakeMagma(second[A]) +} + +func endo[A any](f func(A) A, c func(A, A) A, x, y A) A { + return c(f(x), f(y)) +} + +func Endo[A any](f func(A) A) func(Magma[A]) Magma[A] { + return func(m Magma[A]) Magma[A] { + c := m.Concat + return MakeMagma(func(x A, y A) A { + return endo(f, c, x, y) + }) + } +} diff --git a/v2/magma/magma_test.go b/v2/magma/magma_test.go new file mode 100644 index 0000000..6e60e28 --- /dev/null +++ b/v2/magma/magma_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package magma + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFirst(t *testing.T) { + m := First[string]() + + assert.Equal(t, "a", m.Concat("a", "b")) +} + +func TestSecond(t *testing.T) { + m := Second[string]() + + assert.Equal(t, "b", m.Concat("a", "b")) +} diff --git a/v2/main.go b/v2/main.go new file mode 100644 index 0000000..fb9751e --- /dev/null +++ b/v2/main.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package main contains the entry point for the code generator +package main + +import ( + "log" + "os" + + "github.com/IBM/fp-go/v2/cli" + + C "github.com/urfave/cli/v2" +) + +func main() { + + app := &C.App{ + Name: "fp-go", + Usage: "Code generation for fp-go", + Commands: cli.Commands(), + } + + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } +} diff --git a/v2/monoid/alt.go b/v2/monoid/alt.go new file mode 100644 index 0000000..9da145f --- /dev/null +++ b/v2/monoid/alt.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monoid + +import ( + S "github.com/IBM/fp-go/v2/semigroup" +) + +func AlternativeMonoid[A, HKTA, HKTFA any, LAZYHKTA ~func() HKTA]( + fof func(A) HKTA, + + fmap func(HKTA, func(A) func(A) A) HKTFA, + fap func(HKTFA, HKTA) HKTA, + + falt func(HKTA, LAZYHKTA) HKTA, + + m Monoid[A], + +) Monoid[HKTA] { + + sg := ApplicativeMonoid(fof, fmap, fap, m) + + return MakeMonoid( + func(first, second HKTA) HKTA { + snd := func() HKTA { return second } + + return falt(sg.Concat(first, second), func() HKTA { + return falt(first, snd) + }) + }, + sg.Empty(), + ) +} + +func AltMonoid[HKTA any, LAZYHKTA ~func() HKTA]( + fzero LAZYHKTA, + falt func(HKTA, LAZYHKTA) HKTA, + +) Monoid[HKTA] { + + return MakeMonoid( + S.AltSemigroup(falt).Concat, + fzero(), + ) +} diff --git a/v2/monoid/apply.go b/v2/monoid/apply.go new file mode 100644 index 0000000..f6384c4 --- /dev/null +++ b/v2/monoid/apply.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monoid + +import ( + S "github.com/IBM/fp-go/v2/semigroup" +) + +func ApplicativeMonoid[A, HKTA, HKTFA any]( + fof func(A) HKTA, + fmap func(HKTA, func(A) func(A) A) HKTFA, + fap func(HKTFA, HKTA) HKTA, + + m Monoid[A], +) Monoid[HKTA] { + + return MakeMonoid( + S.ApplySemigroup[A](fmap, fap, m).Concat, + fof(m.Empty()), + ) +} diff --git a/v2/monoid/array.go b/v2/monoid/array.go new file mode 100644 index 0000000..1d63f4a --- /dev/null +++ b/v2/monoid/array.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monoid + +import ( + S "github.com/IBM/fp-go/v2/semigroup" +) + +func GenericConcatAll[GA ~[]A, A any](m Monoid[A]) func(GA) A { + return S.GenericConcatAll[GA](S.MakeSemigroup(m.Concat))(m.Empty()) +} + +// ConcatAll concatenates all values using the monoid and the default empty value +func ConcatAll[A any](m Monoid[A]) func([]A) A { + return GenericConcatAll[[]A](m) +} + +// Fold concatenates all values using the monoid and the default empty value +func Fold[A any](m Monoid[A]) func([]A) A { + return GenericConcatAll[[]A](m) +} diff --git a/v2/monoid/function.go b/v2/monoid/function.go new file mode 100644 index 0000000..9260481 --- /dev/null +++ b/v2/monoid/function.go @@ -0,0 +1,29 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monoid + +import ( + F "github.com/IBM/fp-go/v2/function" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// FunctionMonoid forms a monoid as long as you can provide a monoid for the codomain. +func FunctionMonoid[A, B any](m Monoid[B]) Monoid[func(A) B] { + return MakeMonoid( + S.FunctionSemigroup[A, B](m).Concat, + F.Constant1[A](m.Empty()), + ) +} diff --git a/v2/monoid/monoid.go b/v2/monoid/monoid.go new file mode 100644 index 0000000..032ccb5 --- /dev/null +++ b/v2/monoid/monoid.go @@ -0,0 +1,52 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monoid + +import ( + S "github.com/IBM/fp-go/v2/semigroup" +) + +type Monoid[A any] interface { + S.Semigroup[A] + Empty() A +} + +type monoid[A any] struct { + c func(A, A) A + e A +} + +func (m monoid[A]) Concat(x, y A) A { + return m.c(x, y) +} + +func (m monoid[A]) Empty() A { + return m.e +} + +// MakeMonoid creates a monoid given a concat function and an empty element +func MakeMonoid[A any](c func(A, A) A, e A) Monoid[A] { + return monoid[A]{c: c, e: e} +} + +// Reverse returns the dual of a `Monoid`, obtained by swapping the arguments of `Concat`. +func Reverse[A any](m Monoid[A]) Monoid[A] { + return MakeMonoid(S.Reverse[A](m).Concat, m.Empty()) +} + +func ToSemigroup[A any](m Monoid[A]) S.Semigroup[A] { + return S.Semigroup[A](m) +} diff --git a/v2/monoid/testing/rules.go b/v2/monoid/testing/rules.go new file mode 100644 index 0000000..969dd40 --- /dev/null +++ b/v2/monoid/testing/rules.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + AR "github.com/IBM/fp-go/v2/internal/array" + M "github.com/IBM/fp-go/v2/monoid" + "github.com/stretchr/testify/assert" +) + +func assertLaws[A any](t *testing.T, m M.Monoid[A]) func(a A) bool { + e := m.Empty() + return func(a A) bool { + return assert.Equal(t, a, m.Concat(a, e), "Monoid right identity") && + assert.Equal(t, a, m.Concat(e, a), "Monoid left identity") + } +} + +// AssertLaws asserts the monoid laws for a dataset +func AssertLaws[A any](t *testing.T, m M.Monoid[A]) func(data []A) bool { + law := assertLaws(t, m) + + return func(data []A) bool { + return AR.Reduce(data, func(result bool, value A) bool { + return result && law(value) + }, true) + } +} diff --git a/v2/number/integer/ord.go b/v2/number/integer/ord.go new file mode 100644 index 0000000..5600f1a --- /dev/null +++ b/v2/number/integer/ord.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integer + +import ( + O "github.com/IBM/fp-go/v2/ord" +) + +// Ord is the strict ordering for integers +var Ord = O.FromStrictCompare[int]() + +// Between checks if an integer is between two values +var Between = O.Between[int](Ord) diff --git a/v2/number/integer/string.go b/v2/number/integer/string.go new file mode 100644 index 0000000..3cc5489 --- /dev/null +++ b/v2/number/integer/string.go @@ -0,0 +1,23 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integer + +import "strconv" + +var ( + // ToString converts an integer to a string + ToString = strconv.Itoa +) diff --git a/v2/number/magma.go b/v2/number/magma.go new file mode 100644 index 0000000..533622b --- /dev/null +++ b/v2/number/magma.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package number + +import ( + M "github.com/IBM/fp-go/v2/magma" +) + +func MagmaSub[A Number]() M.Magma[A] { + return M.MakeMagma(func(first A, second A) A { + return first - second + }) +} + +func MagmaDiv[A Number]() M.Magma[A] { + return M.MakeMagma(func(first A, second A) A { + return first / second + }) +} diff --git a/v2/number/magma_test.go b/v2/number/magma_test.go new file mode 100644 index 0000000..3fb003a --- /dev/null +++ b/v2/number/magma_test.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package number + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + M "github.com/IBM/fp-go/v2/magma" +) + +func TestSemigroupIsMagma(t *testing.T) { + sum := SemigroupSum[int]() + + var magma M.Magma[int] = sum + + assert.Equal(t, sum.Concat(1, 2), magma.Concat(1, 2)) + assert.Equal(t, sum.Concat(1, 2), sum.Concat(2, 1)) +} diff --git a/v2/number/monoid.go b/v2/number/monoid.go new file mode 100644 index 0000000..18e5029 --- /dev/null +++ b/v2/number/monoid.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package number + +import ( + M "github.com/IBM/fp-go/v2/monoid" +) + +// MonoidSum is the [Monoid] that adds elements with a zero empty element +func MonoidSum[A Number]() M.Monoid[A] { + s := SemigroupSum[A]() + return M.MakeMonoid( + s.Concat, + 0, + ) +} + +// MonoidProduct is the [Monoid] that multiplies elements with a one empty element +func MonoidProduct[A Number]() M.Monoid[A] { + s := SemigroupProduct[A]() + return M.MakeMonoid( + s.Concat, + 1, + ) +} diff --git a/v2/number/monoid_test.go b/v2/number/monoid_test.go new file mode 100644 index 0000000..7272cfa --- /dev/null +++ b/v2/number/monoid_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package number + +import ( + "testing" + + M "github.com/IBM/fp-go/v2/monoid/testing" +) + +func TestMonoidSum(t *testing.T) { + M.AssertLaws(t, MonoidSum[int]())([]int{0, 1, 1000, -1}) +} diff --git a/v2/number/semigroup.go b/v2/number/semigroup.go new file mode 100644 index 0000000..caaa581 --- /dev/null +++ b/v2/number/semigroup.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package number + +import ( + S "github.com/IBM/fp-go/v2/semigroup" +) + +func SemigroupSum[A Number]() S.Semigroup[A] { + return S.MakeSemigroup(func(first A, second A) A { + return first + second + }) +} + +func SemigroupProduct[A Number]() S.Semigroup[A] { + return S.MakeSemigroup(func(first A, second A) A { + return first * second + }) +} diff --git a/v2/number/utils.go b/v2/number/utils.go new file mode 100644 index 0000000..c28325f --- /dev/null +++ b/v2/number/utils.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package number + +import ( + C "github.com/IBM/fp-go/v2/constraints" +) + +type Number interface { + C.Integer | C.Float | C.Complex +} + +// Add is a curried function used to add two numbers +func Add[T Number](right T) func(T) T { + return func(left T) T { + return left + right + } +} + +// Sub is a curried function used to subtract two numbers +func Sub[T Number](right T) func(T) T { + return func(left T) T { + return left - right + } +} + +// Mul is a curried function used to multiply two numbers +func Mul[T Number](right T) func(T) T { + return func(left T) T { + return left * right + } +} + +// Div is a curried function used to divide two numbers +func Div[T Number](right T) func(T) T { + return func(left T) T { + return left / right + } +} + +// Inc is a function that increments a number +func Inc[T Number](value T) T { + return value + 1 +} + +// Min takes the minimum of two values. If they are considered equal, the first argument is chosen +func Min[A C.Ordered](a, b A) A { + if a < b { + return a + } + return b +} + +// Max takes the maximum of two values. If they are considered equal, the first argument is chosen +func Max[A C.Ordered](a, b A) A { + if a > b { + return a + } + return b +} diff --git a/v2/optics/README.md b/v2/optics/README.md new file mode 100644 index 0000000..71b4e96 --- /dev/null +++ b/v2/optics/README.md @@ -0,0 +1,4 @@ +# Optics + +Refer to [Introduction to optics: lenses and prisms](https://medium.com/@gcanti/introduction-to-optics-lenses-and-prisms-3230e73bfcfe) for an introduction about functional optics. + diff --git a/v2/optics/iso/iso.go b/v2/optics/iso/iso.go new file mode 100644 index 0000000..4c144b5 --- /dev/null +++ b/v2/optics/iso/iso.go @@ -0,0 +1,106 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Iso is an optic which converts elements of type `S` into elements of type `A` without loss. +package iso + +import ( + EM "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" +) + +type Iso[S, A any] struct { + Get func(s S) A + ReverseGet func(a A) S +} + +func MakeIso[S, A any](get func(S) A, reverse func(A) S) Iso[S, A] { + return Iso[S, A]{Get: get, ReverseGet: reverse} +} + +// Id returns an iso implementing the identity operation +func Id[S any]() Iso[S, S] { + return MakeIso(F.Identity[S], F.Identity[S]) +} + +// Compose combines an ISO with another ISO +func Compose[S, A, B any](ab Iso[A, B]) func(Iso[S, A]) Iso[S, B] { + return func(sa Iso[S, A]) Iso[S, B] { + return MakeIso( + F.Flow2(sa.Get, ab.Get), + F.Flow2(ab.ReverseGet, sa.ReverseGet), + ) + } +} + +// Reverse changes the order of parameters for an iso +func Reverse[S, A any](sa Iso[S, A]) Iso[A, S] { + return MakeIso( + sa.ReverseGet, + sa.Get, + ) +} + +func modify[FCT ~func(A) A, S, A any](f FCT, sa Iso[S, A], s S) S { + return F.Pipe3( + s, + sa.Get, + f, + sa.ReverseGet, + ) +} + +// Modify applies a transformation +func Modify[S any, FCT ~func(A) A, A any](f FCT) func(Iso[S, A]) EM.Endomorphism[S] { + return EM.Curry3(modify[FCT, S, A])(f) +} + +// Wrap wraps the value +func Unwrap[A, S any](s S) func(Iso[S, A]) A { + return func(sa Iso[S, A]) A { + return sa.Get(s) + } +} + +// Unwrap unwraps the value +func Wrap[S, A any](a A) func(Iso[S, A]) S { + return func(sa Iso[S, A]) S { + return sa.ReverseGet(a) + } +} + +// From wraps the value +func To[A, S any](s S) func(Iso[S, A]) A { + return Unwrap[A, S](s) +} + +// To unwraps the value +func From[S, A any](a A) func(Iso[S, A]) S { + return Wrap[S](a) +} + +func imap[S, A, B any](sa Iso[S, A], ab func(A) B, ba func(B) A) Iso[S, B] { + return MakeIso( + F.Flow2(sa.Get, ab), + F.Flow2(ba, sa.ReverseGet), + ) +} + +// IMap implements a bidirectional mapping of the transform +func IMap[S, A, B any](ab func(A) B, ba func(B) A) func(Iso[S, A]) Iso[S, B] { + return func(sa Iso[S, A]) Iso[S, B] { + return imap(sa, ab, ba) + } +} diff --git a/v2/optics/iso/iso_test.go b/v2/optics/iso/iso_test.go new file mode 100644 index 0000000..efcdbbc --- /dev/null +++ b/v2/optics/iso/iso_test.go @@ -0,0 +1,79 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iso + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + mToKm = MakeIso( + func(m float32) float32 { + return m / 1000 + }, + func(km float32) float32 { + return km * 1000 + }, + ) + + kmToMile = MakeIso( + func(km float32) float32 { + return km * 0.621371 + }, + func(mile float32) float32 { + return mile / 0.621371 + }, + ) +) + +func TestGet(t *testing.T) { + assert.Equal(t, mToKm.Get(100), float32(0.1)) + assert.Equal(t, Unwrap[float32, float32](float32(100))(mToKm), float32(0.1)) + assert.Equal(t, To[float32, float32](float32(100))(mToKm), float32(0.1)) +} + +func TestReverseGet(t *testing.T) { + assert.Equal(t, mToKm.ReverseGet(1.2), float32(1200)) + assert.Equal(t, Wrap[float32](float32(1.2))(mToKm), float32(1200)) + assert.Equal(t, From[float32](float32(1.2))(mToKm), float32(1200)) +} + +func TestModify(t *testing.T) { + + double := func(x float32) float32 { + return x * 2 + } + + assert.Equal(t, float32(2000), Modify[float32](double)(mToKm)(float32(1000))) +} + +func TestReverse(t *testing.T) { + + double := func(x float32) float32 { + return x * 2 + } + + assert.Equal(t, float32(4000), Modify[float32](double)(Reverse(mToKm))(float32(2000))) +} + +func TestCompose(t *testing.T) { + comp := Compose[float32](mToKm)(kmToMile) + + assert.InDelta(t, 0.93, comp.Get(1500), 0.01) + assert.InDelta(t, 1609.34, comp.ReverseGet(1), 0.01) +} diff --git a/v2/optics/iso/lens/lens.go b/v2/optics/iso/lens/lens.go new file mode 100644 index 0000000..1a18468 --- /dev/null +++ b/v2/optics/iso/lens/lens.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lens + +import ( + EM "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/optics/iso" + L "github.com/IBM/fp-go/v2/optics/lens" +) + +// IsoAsLens converts an `Iso` to a `Lens` +func IsoAsLens[S, A any](sa I.Iso[S, A]) L.Lens[S, A] { + return L.MakeLensCurried(sa.Get, F.Flow2(sa.ReverseGet, F.Flow2(F.Constant1[S, S], EM.Of[func(S) S]))) +} + +// IsoAsLensRef converts an `Iso` to a `Lens` +func IsoAsLensRef[S, A any](sa I.Iso[*S, A]) L.Lens[*S, A] { + return L.MakeLensRefCurried(sa.Get, F.Flow2(sa.ReverseGet, F.Flow2(F.Constant1[*S, *S], EM.Of[func(*S) *S]))) +} diff --git a/v2/optics/lens/array/generic/head.go b/v2/optics/lens/array/generic/head.go new file mode 100644 index 0000000..ad53ca5 --- /dev/null +++ b/v2/optics/lens/array/generic/head.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + AA "github.com/IBM/fp-go/v2/array/generic" + L "github.com/IBM/fp-go/v2/optics/lens" + O "github.com/IBM/fp-go/v2/option" +) + +// AtHead focusses on the head of an array. The setter works as follows +// - if the new value is none, the result will be an empty array +// - if the new value is some and the array is empty, it creates a new array with one element +// - if the new value is some and the array is not empty, it replaces the head +func AtHead[AS []A, A any]() L.Lens[AS, O.Option[A]] { + return L.MakeLens(AA.Head[AS, A], func(as AS, a O.Option[A]) AS { + return O.MonadFold(a, AA.Empty[AS], func(v A) AS { + if AA.IsEmpty(as) { + return AA.Of[AS, A](v) + } + cpy := AA.Copy(as) + cpy[0] = v + return cpy + }) + }) +} diff --git a/v2/optics/lens/array/head.go b/v2/optics/lens/array/head.go new file mode 100644 index 0000000..55ad304 --- /dev/null +++ b/v2/optics/lens/array/head.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + L "github.com/IBM/fp-go/v2/optics/lens" + G "github.com/IBM/fp-go/v2/optics/lens/array/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// AtHead focusses on the head of an array. The setter works as follows +// - if the new value is none, the result will be an empty array +// - if the new value is some and the array is empty, it creates a new array with one element +// - if the new value is some and the array is not empty, it replaces the head +func AtHead[A any]() L.Lens[[]A, O.Option[A]] { + return G.AtHead[[]A]() +} diff --git a/v2/optics/lens/array/head_test.go b/v2/optics/lens/array/head_test.go new file mode 100644 index 0000000..ec57b28 --- /dev/null +++ b/v2/optics/lens/array/head_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + "github.com/IBM/fp-go/v2/eq" + LT "github.com/IBM/fp-go/v2/optics/lens/testing" + O "github.com/IBM/fp-go/v2/option" + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +var ( + sEq = eq.FromEquals(S.Eq) +) + +func TestLaws(t *testing.T) { + headLaws := LT.AssertLaws(t, O.Eq(sEq), A.Eq(sEq))(AtHead[string]()) + + assert.True(t, headLaws(A.Empty[string](), O.None[string]())) + assert.True(t, headLaws(A.Empty[string](), O.Of("a"))) + assert.True(t, headLaws(A.From("a", "b"), O.None[string]())) + assert.True(t, headLaws(A.From("a", "b"), O.Of("c"))) +} diff --git a/v2/optics/lens/either/either.go b/v2/optics/lens/either/either.go new file mode 100644 index 0000000..bfcaaf3 --- /dev/null +++ b/v2/optics/lens/either/either.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + ET "github.com/IBM/fp-go/v2/either" + L "github.com/IBM/fp-go/v2/optics/lens" + LG "github.com/IBM/fp-go/v2/optics/lens/generic" + T "github.com/IBM/fp-go/v2/optics/traversal/either" +) + +func AsTraversal[E, S, A any]() func(L.Lens[S, A]) T.Traversal[E, S, A] { + return LG.AsTraversal[T.Traversal[E, S, A]](ET.MonadMap[E, A, S]) +} diff --git a/v2/optics/lens/generic/lens.go b/v2/optics/lens/generic/lens.go new file mode 100644 index 0000000..0b5a377 --- /dev/null +++ b/v2/optics/lens/generic/lens.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + L "github.com/IBM/fp-go/v2/optics/lens" +) + +// AsTraversal converts a lens to a traversal +func AsTraversal[R ~func(func(A) HKTA) func(S) HKTS, S, A, HKTS, HKTA any]( + fmap func(HKTA, func(A) S) HKTS, +) func(L.Lens[S, A]) R { + return func(sa L.Lens[S, A]) R { + return func(f func(a A) HKTA) func(S) HKTS { + return func(s S) HKTS { + return fmap(f(sa.Get(s)), func(a A) S { + return sa.Set(a)(s) + }) + } + } + } +} diff --git a/v2/optics/lens/iso/iso.go b/v2/optics/lens/iso/iso.go new file mode 100644 index 0000000..e014f93 --- /dev/null +++ b/v2/optics/lens/iso/iso.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iso + +import ( + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/optics/iso" + IL "github.com/IBM/fp-go/v2/optics/iso/lens" + L "github.com/IBM/fp-go/v2/optics/lens" + O "github.com/IBM/fp-go/v2/option" +) + +// FromNillable converts a nillable value to an option and back +func FromNillable[T any]() I.Iso[*T, O.Option[T]] { + return I.MakeIso(F.Flow2( + O.FromPredicate(F.IsNonNil[T]), + O.Map(F.Deref[T]), + ), + O.Fold(F.Constant((*T)(nil)), F.Ref[T]), + ) +} + +// Compose converts a Lens to a property of `A` into a lens to a property of type `B` +// the transformation is done via an ISO +func Compose[S, A, B any](ab I.Iso[A, B]) func(sa L.Lens[S, A]) L.Lens[S, B] { + return F.Pipe2( + ab, + IL.IsoAsLens[A, B], + L.Compose[S, A, B], + ) +} diff --git a/v2/optics/lens/iso/iso_test.go b/v2/optics/lens/iso/iso_test.go new file mode 100644 index 0000000..fd5c63b --- /dev/null +++ b/v2/optics/lens/iso/iso_test.go @@ -0,0 +1,99 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iso + +import ( + "testing" + + EQT "github.com/IBM/fp-go/v2/eq/testing" + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/optics/lens" + LT "github.com/IBM/fp-go/v2/optics/lens/testing" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type ( + Inner struct { + Value *int + Foo string + } + + Outer struct { + inner Inner + } +) + +func (outer Outer) GetInner() Inner { + return outer.inner +} + +func (outer Outer) SetInner(inner Inner) Outer { + outer.inner = inner + return outer +} + +func (inner Inner) GetValue() *int { + return inner.Value +} + +func (inner Inner) SetValue(value *int) Inner { + inner.Value = value + return inner +} + +func TestIso(t *testing.T) { + + eqOptInt := O.Eq(EQT.Eq[int]()) + eqOuter := EQT.Eq[Outer]() + + emptyOuter := Outer{} + + // iso + intIso := FromNillable[int]() + + innerFromOuter := L.MakeLens((Outer).GetInner, (Outer).SetInner) + valueFromInner := L.MakeLens((Inner).GetValue, (Inner).SetValue) + + optValueFromInner := F.Pipe1( + valueFromInner, + Compose[Inner](intIso), + ) + + optValueFromOuter := F.Pipe1( + innerFromOuter, + L.Compose[Outer](optValueFromInner), + ) + + // try some access + require.True(t, eqOptInt.Equals(optValueFromOuter.Get(emptyOuter), O.None[int]())) + + updatedOuter := optValueFromOuter.Set(O.Some(1))(emptyOuter) + + require.True(t, eqOptInt.Equals(optValueFromOuter.Get(updatedOuter), O.Some(1))) + secondOuter := optValueFromOuter.Set(O.None[int]())(updatedOuter) + require.True(t, eqOptInt.Equals(optValueFromOuter.Get(secondOuter), O.None[int]())) + + // check if this obeys laws + laws := LT.AssertLaws( + t, + eqOptInt, + eqOuter, + )(optValueFromOuter) + + assert.True(t, laws(emptyOuter, O.Some(2))) +} diff --git a/v2/optics/lens/lens.go b/v2/optics/lens/lens.go new file mode 100644 index 0000000..f45c03b --- /dev/null +++ b/v2/optics/lens/lens.go @@ -0,0 +1,325 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Lens is an optic used to zoom inside a product. +package lens + +import ( + EM "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +type ( + // Lens is a reference to a subpart of a data type + Lens[S, A any] struct { + Get func(s S) A + Set func(a A) EM.Endomorphism[S] + } +) + +// setCopy wraps a setter for a pointer into a setter that first creates a copy before +// modifying that copy +func setCopy[SET ~func(*S, A) *S, S, A any](setter SET) func(s *S, a A) *S { + return func(s *S, a A) *S { + cpy := *s + return setter(&cpy, a) + } +} + +// setCopyCurried wraps a setter for a pointer into a setter that first creates a copy before +// modifying that copy +func setCopyCurried[SET ~func(A) EM.Endomorphism[*S], S, A any](setter SET) func(a A) EM.Endomorphism[*S] { + return func(a A) EM.Endomorphism[*S] { + seta := setter(a) + return func(s *S) *S { + cpy := *s + return seta(&cpy) + } + } +} + +// MakeLens creates a [Lens] based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the +// data. This happens automatically if the data is passed by value. For pointers consider to use `MakeLensRef` +// and for other kinds of data structures that are copied by reference make sure the setter creates the copy. +func MakeLens[GET ~func(S) A, SET ~func(S, A) S, S, A any](get GET, set SET) Lens[S, A] { + return MakeLensCurried(get, EM.Curry2(F.Swap(set))) +} + +// MakeLensCurried creates a [Lens] based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the +// data. This happens automatically if the data is passed by value. For pointers consider to use `MakeLensRef` +// and for other kinds of data structures that are copied by reference make sure the setter creates the copy. +func MakeLensCurried[GET ~func(S) A, SET ~func(A) EM.Endomorphism[S], S, A any](get GET, set SET) Lens[S, A] { + return Lens[S, A]{Get: get, Set: set} +} + +// MakeLensRef creates a [Lens] based on a getter and a setter function. The setter passed in does not have to create a shallow +// copy, the implementation wraps the setter into one that copies the pointer before modifying it +// +// Such a [Lens] assumes that property A of S always exists +func MakeLensRef[GET ~func(*S) A, SET func(*S, A) *S, S, A any](get GET, set SET) Lens[*S, A] { + return MakeLens(get, setCopy(set)) +} + +// MakeLensRefCurried creates a [Lens] based on a getter and a setter function. The setter passed in does not have to create a shallow +// copy, the implementation wraps the setter into one that copies the pointer before modifying it +// +// Such a [Lens] assumes that property A of S always exists +func MakeLensRefCurried[S, A any](get func(*S) A, set func(A) EM.Endomorphism[*S]) Lens[*S, A] { + return MakeLensCurried(get, setCopyCurried(set)) +} + +// id returns a [Lens] implementing the identity operation +func id[GET ~func(S) S, SET ~func(S, S) S, S any](creator func(get GET, set SET) Lens[S, S]) Lens[S, S] { + return creator(F.Identity[S], F.Second[S, S]) +} + +// Id returns a [Lens] implementing the identity operation +func Id[S any]() Lens[S, S] { + return id(MakeLens[EM.Endomorphism[S], func(S, S) S]) +} + +// IdRef returns a [Lens] implementing the identity operation +func IdRef[S any]() Lens[*S, *S] { + return id(MakeLensRef[EM.Endomorphism[*S], func(*S, *S) *S]) +} + +// Compose combines two lenses and allows to narrow down the focus to a sub-lens +func compose[GET ~func(S) B, SET ~func(S, B) S, S, A, B any](creator func(get GET, set SET) Lens[S, B], ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] { + abget := ab.Get + abset := ab.Set + return func(sa Lens[S, A]) Lens[S, B] { + saget := sa.Get + saset := sa.Set + return creator( + F.Flow2(saget, abget), + func(s S, b B) S { + return saset(abset(b)(saget(s)))(s) + }, + ) + } +} + +// Compose combines two lenses and allows to narrow down the focus to a sub-lens +func Compose[S, A, B any](ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] { + return compose(MakeLens[func(S) B, func(S, B) S], ab) +} + +// ComposeOption combines a `Lens` that returns an optional value with a `Lens` that returns a definite value +// the getter returns an `Option[B]` because the container `A` could already be an option +// if the setter is invoked with `Some[B]` then the value of `B` will be set, potentially on a default value of `A` if `A` did not exist +// if the setter is invoked with `None[B]` then the container `A` is reset to `None[A]` because this is the only way to remove `B` +func ComposeOption[S, B, A any](defaultA A) func(ab Lens[A, B]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] { + defa := F.Constant(defaultA) + return func(ab Lens[A, B]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] { + foldab := O.Fold(O.None[B], F.Flow2(ab.Get, O.Some[B])) + return func(sa Lens[S, O.Option[A]]) Lens[S, O.Option[B]] { + // set A on S + seta := F.Flow2( + O.Some[A], + sa.Set, + ) + // remove A from S + unseta := F.Nullary2( + O.None[A], + sa.Set, + ) + return MakeLens( + F.Flow2(sa.Get, foldab), + func(s S, ob O.Option[B]) S { + return F.Pipe2( + ob, + O.Fold(unseta, func(b B) EM.Endomorphism[S] { + setbona := F.Flow2( + ab.Set(b), + seta, + ) + return F.Pipe2( + s, + sa.Get, + O.Fold( + F.Nullary2( + defa, + setbona, + ), + setbona, + ), + ) + }), + EM.Ap(s), + ) + }, + ) + } + } +} + +// ComposeOptions combines a `Lens` that returns an optional value with a `Lens` that returns another optional value +// the getter returns `None[B]` if either `A` or `B` is `None` +// if the setter is called with `Some[B]` and `A` exists, 'A' is updated with `B` +// if the setter is called with `Some[B]` and `A` does not exist, the default of 'A' is updated with `B` +// if the setter is called with `None[B]` and `A` does not exist this is the identity operation on 'S' +// if the setter is called with `None[B]` and `A` does exist, 'B' is removed from 'A' +func ComposeOptions[S, B, A any](defaultA A) func(ab Lens[A, O.Option[B]]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] { + defa := F.Constant(defaultA) + noops := EM.Identity[S] + noneb := O.None[B]() + return func(ab Lens[A, O.Option[B]]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] { + unsetb := ab.Set(noneb) + return func(sa Lens[S, O.Option[A]]) Lens[S, O.Option[B]] { + // sets an A onto S + seta := F.Flow2( + O.Some[A], + sa.Set, + ) + return MakeLensCurried( + F.Flow2( + sa.Get, + O.Chain(ab.Get), + ), + func(b O.Option[B]) EM.Endomorphism[S] { + return func(s S) S { + return O.MonadFold(b, func() EM.Endomorphism[S] { + return F.Pipe2( + s, + sa.Get, + O.Fold(noops, F.Flow2(unsetb, seta)), + ) + }, func(b B) EM.Endomorphism[S] { + // sets a B onto an A + setb := F.Flow2( + ab.Set(O.Some(b)), + seta, + ) + return F.Pipe2( + s, + sa.Get, + O.Fold(F.Nullary2(defa, setb), setb), + ) + })(s) + } + }, + ) + } + } +} + +// Compose combines two lenses and allows to narrow down the focus to a sub-lens +func ComposeRef[S, A, B any](ab Lens[A, B]) func(Lens[*S, A]) Lens[*S, B] { + return compose(MakeLensRef[func(*S) B, func(*S, B) *S], ab) +} + +func modify[FCT ~func(A) A, S, A any](f FCT, sa Lens[S, A], s S) S { + return sa.Set(f(sa.Get(s)))(s) +} + +// Modify changes a property of a [Lens] by invoking a transformation function +// if the transformed property has not changes, the method returns the original state +func Modify[S any, FCT ~func(A) A, A any](f FCT) func(Lens[S, A]) EM.Endomorphism[S] { + return EM.Curry3(modify[FCT, S, A])(f) +} + +func IMap[E any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) func(Lens[E, A]) Lens[E, B] { + return func(ea Lens[E, A]) Lens[E, B] { + return Lens[E, B]{Get: F.Flow2(ea.Get, ab), Set: F.Flow2(ba, ea.Set)} + } +} + +// fromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional +// if the optional value is set then the nil value will be set instead +func fromPredicate[GET ~func(S) O.Option[A], SET ~func(S, O.Option[A]) S, S, A any](creator func(get GET, set SET) Lens[S, O.Option[A]], pred func(A) bool, nilValue A) func(sa Lens[S, A]) Lens[S, O.Option[A]] { + fromPred := O.FromPredicate(pred) + return func(sa Lens[S, A]) Lens[S, O.Option[A]] { + fold := O.Fold(F.Bind1of1(sa.Set)(nilValue), sa.Set) + return creator(F.Flow2(sa.Get, fromPred), func(s S, a O.Option[A]) S { + return F.Pipe2( + a, + fold, + EM.Ap(s), + ) + }) + } +} + +// FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional +// if the optional value is set then the nil value will be set instead +func FromPredicate[S, A any](pred func(A) bool, nilValue A) func(sa Lens[S, A]) Lens[S, O.Option[A]] { + return fromPredicate(MakeLens[func(S) O.Option[A], func(S, O.Option[A]) S], pred, nilValue) +} + +// FromPredicateRef returns a `Lens` for a property accessibly as a getter and setter that can be optional +// if the optional value is set then the nil value will be set instead +func FromPredicateRef[S, A any](pred func(A) bool, nilValue A) func(sa Lens[*S, A]) Lens[*S, O.Option[A]] { + return fromPredicate(MakeLensRef[func(*S) O.Option[A], func(*S, O.Option[A]) *S], pred, nilValue) +} + +// FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional +// if the optional value is set then the `nil` value will be set instead +func FromNillable[S, A any](sa Lens[S, *A]) Lens[S, O.Option[*A]] { + return FromPredicate[S](F.IsNonNil[A], nil)(sa) +} + +// FromNillableRef returns a `Lens` for a property accessibly as a getter and setter that can be optional +// if the optional value is set then the `nil` value will be set instead +func FromNillableRef[S, A any](sa Lens[*S, *A]) Lens[*S, O.Option[*A]] { + return FromPredicateRef[S](F.IsNonNil[A], nil)(sa) +} + +// fromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items +func fromNullableProp[GET ~func(S) A, SET ~func(S, A) S, S, A any](creator func(get GET, set SET) Lens[S, A], isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] { + return func(sa Lens[S, A]) Lens[S, A] { + return creator(F.Flow3( + sa.Get, + isNullable, + O.GetOrElse(F.Constant(defaultValue)), + ), func(s S, a A) S { + return sa.Set(a)(s) + }, + ) + } +} + +// FromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items +func FromNullableProp[S, A any](isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] { + return fromNullableProp(MakeLens[func(S) A, func(S, A) S], isNullable, defaultValue) +} + +// FromNullablePropRef returns a `Lens` from a property that may be optional. The getter returns a default value for these items +func FromNullablePropRef[S, A any](isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[*S, A]) Lens[*S, A] { + return fromNullableProp(MakeLensRef[func(*S) A, func(*S, A) *S], isNullable, defaultValue) +} + +// fromFromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option +func fromOption[GET ~func(S) A, SET ~func(S, A) S, S, A any](creator func(get GET, set SET) Lens[S, A], defaultValue A) func(sa Lens[S, O.Option[A]]) Lens[S, A] { + return func(sa Lens[S, O.Option[A]]) Lens[S, A] { + return creator(F.Flow2( + sa.Get, + O.GetOrElse(F.Constant(defaultValue)), + ), func(s S, a A) S { + return sa.Set(O.Some(a))(s) + }, + ) + } +} + +// FromFromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option +func FromOption[S, A any](defaultValue A) func(sa Lens[S, O.Option[A]]) Lens[S, A] { + return fromOption(MakeLens[func(S) A, func(S, A) S], defaultValue) +} + +// FromFromOptionRef returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option +func FromOptionRef[S, A any](defaultValue A) func(sa Lens[*S, O.Option[A]]) Lens[*S, A] { + return fromOption(MakeLensRef[func(*S) A, func(*S, A) *S], defaultValue) +} diff --git a/v2/optics/lens/lens_test.go b/v2/optics/lens/lens_test.go new file mode 100644 index 0000000..af5edee --- /dev/null +++ b/v2/optics/lens/lens_test.go @@ -0,0 +1,250 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lens + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +type ( + Street struct { + num int + name string + } + + Address struct { + city string + street *Street + } + + Inner struct { + Value int + Foo string + } + + InnerOpt struct { + Value *int + Foo *string + } + + Outer struct { + inner *Inner + } + + OuterOpt struct { + inner *InnerOpt + } +) + +func (outer Outer) GetInner() *Inner { + return outer.inner +} + +func (outer Outer) SetInner(inner *Inner) Outer { + outer.inner = inner + return outer +} + +func (outer OuterOpt) GetInnerOpt() *InnerOpt { + return outer.inner +} + +func (outer OuterOpt) SetInnerOpt(inner *InnerOpt) OuterOpt { + outer.inner = inner + return outer +} + +func (inner *Inner) GetValue() int { + return inner.Value +} + +func (inner *Inner) SetValue(value int) *Inner { + inner.Value = value + return inner +} + +func (inner *InnerOpt) GetValue() *int { + return inner.Value +} + +func (inner *InnerOpt) SetValue(value *int) *InnerOpt { + inner.Value = value + return inner +} + +func (street *Street) GetName() string { + return street.name +} + +func (street *Street) SetName(name string) *Street { + street.name = name + return street +} + +func (addr *Address) GetStreet() *Street { + return addr.street +} + +func (addr *Address) SetStreet(s *Street) *Address { + addr.street = s + return addr +} + +var ( + streetLens = MakeLensRef((*Street).GetName, (*Street).SetName) + addrLens = MakeLensRef((*Address).GetStreet, (*Address).SetStreet) + + sampleStreet = Street{num: 220, name: "Schönaicherstr"} + sampleAddress = Address{city: "Böblingen", street: &sampleStreet} +) + +func TestLens(t *testing.T) { + // read the value + assert.Equal(t, sampleStreet.name, streetLens.Get(&sampleStreet)) + // new street + newName := "Böblingerstr" + // update + old := sampleStreet + updated := streetLens.Set(newName)(&sampleStreet) + assert.Equal(t, old, sampleStreet) + // validate the new name + assert.Equal(t, newName, streetLens.Get(updated)) +} + +func TestAddressCompose(t *testing.T) { + // compose + streetName := Compose[*Address](streetLens)(addrLens) + assert.Equal(t, sampleStreet.name, streetName.Get(&sampleAddress)) + // new street + newName := "Böblingerstr" + updated := streetName.Set(newName)(&sampleAddress) + // check that we have not modified the original + assert.Equal(t, sampleStreet.name, streetName.Get(&sampleAddress)) + assert.Equal(t, newName, streetName.Get(updated)) +} + +func TestIMap(t *testing.T) { + + type S struct { + a int + } + + sa := F.Pipe1( + Id[S](), + IMap[S]( + func(s S) int { return s.a }, + func(a int) S { return S{a} }, + ), + ) + + assert.Equal(t, 1, sa.Get(S{1})) + assert.Equal(t, S{2}, sa.Set(2)(S{1})) +} + +func TestPassByValue(t *testing.T) { + + testLens := MakeLens(func(s Street) string { return s.name }, func(s Street, value string) Street { + s.name = value + return s + }) + + s1 := Street{1, "value1"} + s2 := testLens.Set("value2")(s1) + + assert.Equal(t, "value1", s1.name) + assert.Equal(t, "value2", s2.name) +} + +func TestFromNullableProp(t *testing.T) { + // default inner object + defaultInner := &Inner{ + Value: 0, + Foo: "foo", + } + // access to the value + value := MakeLensRef((*Inner).GetValue, (*Inner).SetValue) + // access to inner + inner := FromNullableProp[Outer](O.FromNillable[Inner], defaultInner)(MakeLens(Outer.GetInner, Outer.SetInner)) + // compose + lens := F.Pipe1( + inner, + Compose[Outer](value), + ) + outer1 := Outer{inner: &Inner{Value: 1, Foo: "a"}} + // the checks + assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(1)(Outer{})) + assert.Equal(t, 0, lens.Get(Outer{})) + assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(1)(Outer{inner: &Inner{Value: 2, Foo: "foo"}})) + assert.Equal(t, 1, lens.Get(Outer{inner: &Inner{Value: 1, Foo: "foo"}})) + assert.Equal(t, outer1, Modify[Outer](F.Identity[int])(lens)(outer1)) +} + +func TestComposeOption(t *testing.T) { + // default inner object + defaultInner := &Inner{ + Value: 0, + Foo: "foo", + } + // access to the value + value := MakeLensRef((*Inner).GetValue, (*Inner).SetValue) + // access to inner + inner := FromNillable(MakeLens(Outer.GetInner, Outer.SetInner)) + // compose lenses + lens := F.Pipe1( + inner, + ComposeOption[Outer, int](defaultInner)(value), + ) + outer1 := Outer{inner: &Inner{Value: 1, Foo: "a"}} + // the checks + assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(O.Some(1))(Outer{})) + assert.Equal(t, O.None[int](), lens.Get(Outer{})) + assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(O.Some(1))(Outer{inner: &Inner{Value: 2, Foo: "foo"}})) + assert.Equal(t, O.Some(1), lens.Get(Outer{inner: &Inner{Value: 1, Foo: "foo"}})) + assert.Equal(t, outer1, Modify[Outer](F.Identity[O.Option[int]])(lens)(outer1)) +} + +func TestComposeOptions(t *testing.T) { + // default inner object + defaultValue1 := 1 + defaultFoo1 := "foo1" + defaultInner := &InnerOpt{ + Value: &defaultValue1, + Foo: &defaultFoo1, + } + // access to the value + value := FromNillable(MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue)) + // access to inner + inner := FromNillable(MakeLens(OuterOpt.GetInnerOpt, OuterOpt.SetInnerOpt)) + // compose lenses + lens := F.Pipe1( + inner, + ComposeOptions[OuterOpt, *int](defaultInner)(value), + ) + // additional settings + defaultValue2 := 2 + defaultFoo2 := "foo2" + outer1 := OuterOpt{inner: &InnerOpt{Value: &defaultValue2, Foo: &defaultFoo2}} + // the checks + assert.Equal(t, OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo1}}, lens.Set(O.Some(&defaultValue1))(OuterOpt{})) + assert.Equal(t, O.None[*int](), lens.Get(OuterOpt{})) + assert.Equal(t, OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo2}}, lens.Set(O.Some(&defaultValue1))(OuterOpt{inner: &InnerOpt{Value: &defaultValue2, Foo: &defaultFoo2}})) + assert.Equal(t, O.Some(&defaultValue1), lens.Get(OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo1}})) + assert.Equal(t, outer1, Modify[OuterOpt](F.Identity[O.Option[*int]])(lens)(outer1)) +} diff --git a/v2/optics/lens/option/option.go b/v2/optics/lens/option/option.go new file mode 100644 index 0000000..2ee1f3a --- /dev/null +++ b/v2/optics/lens/option/option.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + L "github.com/IBM/fp-go/v2/optics/lens" + LG "github.com/IBM/fp-go/v2/optics/lens/generic" + T "github.com/IBM/fp-go/v2/optics/traversal/option" + O "github.com/IBM/fp-go/v2/option" +) + +func AsTraversal[S, A any]() func(L.Lens[S, A]) T.Traversal[S, A] { + return LG.AsTraversal[T.Traversal[S, A]](O.MonadMap[A, S]) +} diff --git a/v2/optics/lens/optional/optional.go b/v2/optics/lens/optional/optional.go new file mode 100644 index 0000000..7a6c3d8 --- /dev/null +++ b/v2/optics/lens/optional/optional.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package optional + +import ( + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/optics/lens" + OPT "github.com/IBM/fp-go/v2/optics/optional" + O "github.com/IBM/fp-go/v2/option" +) + +func lensAsOptional[S, A any](creator func(get func(S) O.Option[A], set func(S, A) S) OPT.Optional[S, A], sa L.Lens[S, A]) OPT.Optional[S, A] { + return creator(F.Flow2(sa.Get, O.Some[A]), func(s S, a A) S { + return sa.Set(a)(s) + }) +} + +// LensAsOptional converts a Lens into an Optional +func LensAsOptional[S, A any](sa L.Lens[S, A]) OPT.Optional[S, A] { + return lensAsOptional(OPT.MakeOptional[S, A], sa) +} diff --git a/v2/optics/lens/record/generic/record.go b/v2/optics/lens/record/generic/record.go new file mode 100644 index 0000000..c871f04 --- /dev/null +++ b/v2/optics/lens/record/generic/record.go @@ -0,0 +1,49 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity" + L "github.com/IBM/fp-go/v2/optics/lens" + O "github.com/IBM/fp-go/v2/option" + RR "github.com/IBM/fp-go/v2/record/generic" +) + +// AtRecord returns a lens that focusses on a value in a record +func AtRecord[M ~map[K]V, V any, K comparable](key K) L.Lens[M, O.Option[V]] { + addKey := F.Bind1of2(RR.UpsertAt[M, K, V])(key) + delKey := F.Bind1of1(RR.DeleteAt[M, K, V])(key) + fold := O.Fold( + delKey, + addKey, + ) + return L.MakeLens( + RR.Lookup[M](key), + func(m M, v O.Option[V]) M { + return F.Pipe2( + v, + fold, + I.Ap[M, M](m), + ) + }, + ) +} + +// AtKey returns a `Lens` focused on a required key of a `ReadonlyRecord` +func AtKey[M ~map[K]V, S any, V any, K comparable](key K) func(sa L.Lens[S, M]) L.Lens[S, O.Option[V]] { + return L.Compose[S](AtRecord[M](key)) +} diff --git a/v2/optics/lens/record/record.go b/v2/optics/lens/record/record.go new file mode 100644 index 0000000..45914ec --- /dev/null +++ b/v2/optics/lens/record/record.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + L "github.com/IBM/fp-go/v2/optics/lens" + G "github.com/IBM/fp-go/v2/optics/lens/record/generic" + O "github.com/IBM/fp-go/v2/option" +) + +// AtRecord returns a lens that focusses on a value in a record +func AtRecord[V any, K comparable](key K) L.Lens[map[K]V, O.Option[V]] { + return G.AtRecord[map[K]V](key) +} + +// AtKey returns a `Lens` focused on a required key of a `ReadonlyRecord` +func AtKey[S any, V any, K comparable](key K) func(sa L.Lens[S, map[K]V]) L.Lens[S, O.Option[V]] { + return G.AtKey[map[K]V, S](key) +} diff --git a/v2/optics/lens/record/record_test.go b/v2/optics/lens/record/record_test.go new file mode 100644 index 0000000..54ee366 --- /dev/null +++ b/v2/optics/lens/record/record_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/optics/lens" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +type ( + S = map[string]int +) + +func TestAtKey(t *testing.T) { + sa := F.Pipe1( + L.Id[S](), + AtKey[S, int]("a"), + ) + + assert.Equal(t, O.Some(1), sa.Get(S{"a": 1})) + assert.Equal(t, S{"a": 2, "b": 2}, sa.Set(O.Some(2))(S{"a": 1, "b": 2})) + assert.Equal(t, S{"a": 1, "b": 2}, sa.Set(O.Some(1))(S{"b": 2})) + assert.Equal(t, S{"b": 2}, sa.Set(O.None[int]())(S{"a": 1, "b": 2})) +} diff --git a/v2/optics/lens/testing/laws.go b/v2/optics/lens/testing/laws.go new file mode 100644 index 0000000..e13fcff --- /dev/null +++ b/v2/optics/lens/testing/laws.go @@ -0,0 +1,80 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + E "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/optics/lens" + "github.com/stretchr/testify/assert" +) + +// LensGet tests the law: +// get(set(a)(s)) = a +func LensGet[S, A any]( + t *testing.T, + eqa E.Eq[A], +) func(l L.Lens[S, A]) func(s S, a A) bool { + + return func(l L.Lens[S, A]) func(s S, a A) bool { + + return func(s S, a A) bool { + return assert.True(t, eqa.Equals(l.Get(l.Set(a)(s)), a), "Lens get(set(a)(s)) = a") + } + } +} + +// LensSet tests the laws: +// set(get(s))(s) = s +// set(a)(set(a)(s)) = set(a)(s) +func LensSet[S, A any]( + t *testing.T, + eqs E.Eq[S], +) func(l L.Lens[S, A]) func(s S, a A) bool { + + return func(l L.Lens[S, A]) func(s S, a A) bool { + + return func(s S, a A) bool { + return assert.True(t, eqs.Equals(l.Set(l.Get(s))(s), s), "Lens set(get(s))(s) = s") && assert.True(t, eqs.Equals(l.Set(a)(l.Set(a)(s)), l.Set(a)(s)), "Lens set(a)(set(a)(s)) = set(a)(s)") + } + } +} + +// AssertLaws tests the lens laws +// +// get(set(a)(s)) = a +// set(get(s))(s) = s +// set(a)(set(a)(s)) = set(a)(s) +func AssertLaws[S, A any]( + t *testing.T, + eqa E.Eq[A], + eqs E.Eq[S], +) func(l L.Lens[S, A]) func(s S, a A) bool { + + lenGet := LensGet[S](t, eqa) + lenSet := LensSet[S, A](t, eqs) + + return func(l L.Lens[S, A]) func(s S, a A) bool { + + get := lenGet(l) + set := lenSet(l) + + return func(s S, a A) bool { + return get(s, a) && set(s, a) + } + } +} diff --git a/v2/optics/lens/testing/laws_test.go b/v2/optics/lens/testing/laws_test.go new file mode 100644 index 0000000..4d44246 --- /dev/null +++ b/v2/optics/lens/testing/laws_test.go @@ -0,0 +1,265 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + EQT "github.com/IBM/fp-go/v2/eq/testing" + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity" + L "github.com/IBM/fp-go/v2/optics/lens" + LI "github.com/IBM/fp-go/v2/optics/lens/iso" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +type ( + Street struct { + num int + name string + } + + Address struct { + city string + street *Street + } + + Inner struct { + Value int + Foo string + } + + InnerOpt struct { + Value *int + Foo *string + } + + Outer struct { + inner *Inner + } + + OuterOpt struct { + inner *InnerOpt + } +) + +func (outer *OuterOpt) GetInner() *InnerOpt { + return outer.inner +} + +func (outer *OuterOpt) SetInner(inner *InnerOpt) *OuterOpt { + outer.inner = inner + return outer +} + +func (inner *InnerOpt) GetValue() *int { + return inner.Value +} + +func (inner *InnerOpt) SetValue(value *int) *InnerOpt { + inner.Value = value + return inner +} + +func (outer *Outer) GetInner() *Inner { + return outer.inner +} + +func (outer *Outer) SetInner(inner *Inner) *Outer { + outer.inner = inner + return outer +} + +func (inner *Inner) GetValue() int { + return inner.Value +} + +func (inner *Inner) SetValue(value int) *Inner { + inner.Value = value + return inner +} + +func (street *Street) GetName() string { + return street.name +} + +func (street *Street) SetName(name string) *Street { + street.name = name + return street +} + +func (addr *Address) GetStreet() *Street { + return addr.street +} + +func (addr *Address) SetStreet(s *Street) *Address { + addr.street = s + return addr +} + +var ( + streetLens = L.MakeLensRef((*Street).GetName, (*Street).SetName) + addrLens = L.MakeLensRef((*Address).GetStreet, (*Address).SetStreet) + outerLens = L.FromNillableRef(L.MakeLensRef((*Outer).GetInner, (*Outer).SetInner)) + valueLens = L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue) + + outerOptLens = L.FromNillableRef(L.MakeLensRef((*OuterOpt).GetInner, (*OuterOpt).SetInner)) + valueOptLens = L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue) + + sampleStreet = Street{num: 220, name: "Schönaicherstr"} + sampleAddress = Address{city: "Böblingen", street: &sampleStreet} + sampleStreet2 = Street{num: 220, name: "Neue Str"} + + defaultInner = Inner{ + Value: -1, + Foo: "foo", + } + + emptyOuter = Outer{} + + defaultInnerOpt = InnerOpt{ + Value: &defaultInner.Value, + Foo: &defaultInner.Foo, + } + + emptyOuterOpt = OuterOpt{} +) + +func TestStreetLensLaws(t *testing.T) { + // some comparison + eqs := EQT.Eq[*Street]() + eqa := EQT.Eq[string]() + + laws := AssertLaws( + t, + eqa, + eqs, + )(streetLens) + + cpy := sampleStreet + assert.True(t, laws(&sampleStreet, "Neue Str.")) + assert.Equal(t, cpy, sampleStreet) +} + +func TestAddrLensLaws(t *testing.T) { + // some comparison + eqs := EQT.Eq[*Address]() + eqa := EQT.Eq[*Street]() + + laws := AssertLaws( + t, + eqa, + eqs, + )(addrLens) + + cpyAddr := sampleAddress + cpyStreet := sampleStreet2 + assert.True(t, laws(&sampleAddress, &sampleStreet2)) + assert.Equal(t, cpyAddr, sampleAddress) + assert.Equal(t, cpyStreet, sampleStreet2) +} + +func TestCompose(t *testing.T) { + // some comparison + eqs := EQT.Eq[*Address]() + eqa := EQT.Eq[string]() + + streetName := L.Compose[*Address](streetLens)(addrLens) + + laws := AssertLaws( + t, + eqa, + eqs, + )(streetName) + + cpyAddr := sampleAddress + cpyStreet := sampleStreet + assert.True(t, laws(&sampleAddress, "Neue Str.")) + assert.Equal(t, cpyAddr, sampleAddress) + assert.Equal(t, cpyStreet, sampleStreet) +} + +func TestOuterLensLaws(t *testing.T) { + // some equal predicates + eqValue := EQT.Eq[int]() + eqOptValue := O.Eq(eqValue) + // lens to access a value from outer + valueFromOuter := L.ComposeOption[*Outer, int](&defaultInner)(valueLens)(outerLens) + // try to access the value, this should get an option + assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]())) + // update the object + withValue := valueFromOuter.Set(O.Some(1))(&emptyOuter) + assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]())) + assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1))) + // updating with none should remove the inner + nextValue := valueFromOuter.Set(O.None[int]())(withValue) + assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]())) + // check if this meets the laws + + eqOuter := EQT.Eq[*Outer]() + + laws := AssertLaws( + t, + eqOptValue, + eqOuter, + )(valueFromOuter) + + assert.True(t, laws(&emptyOuter, O.Some(2))) + assert.True(t, laws(&emptyOuter, O.None[int]())) + + assert.True(t, laws(withValue, O.Some(2))) + assert.True(t, laws(withValue, O.None[int]())) +} + +func TestOuterOptLensLaws(t *testing.T) { + // some equal predicates + eqValue := EQT.Eq[int]() + eqOptValue := O.Eq(eqValue) + intIso := LI.FromNillable[int]() + // lens to access a value from outer + valueFromOuter := F.Pipe3( + valueOptLens, + LI.Compose[*InnerOpt](intIso), + L.ComposeOptions[*OuterOpt, int](&defaultInnerOpt), + I.Ap[L.Lens[*OuterOpt, O.Option[int]]](outerOptLens), + ) + + // try to access the value, this should get an option + assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]())) + // update the object + withValue := valueFromOuter.Set(O.Some(1))(&emptyOuterOpt) + assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]())) + assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1))) + // updating with none should remove the inner + nextValue := valueFromOuter.Set(O.None[int]())(withValue) + assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]())) + // check if this meets the laws + + eqOuter := EQT.Eq[*OuterOpt]() + + laws := AssertLaws( + t, + eqOptValue, + eqOuter, + )(valueFromOuter) + + assert.True(t, laws(&emptyOuterOpt, O.Some(2))) + assert.True(t, laws(&emptyOuterOpt, O.None[int]())) + + assert.True(t, laws(withValue, O.Some(2))) + assert.True(t, laws(withValue, O.None[int]())) +} diff --git a/v2/optics/optional/lens/lens.go b/v2/optics/optional/lens/lens.go new file mode 100644 index 0000000..3356f56 --- /dev/null +++ b/v2/optics/optional/lens/lens.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lens + +import ( + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/optics/lens" + LO "github.com/IBM/fp-go/v2/optics/lens/optional" + OPT "github.com/IBM/fp-go/v2/optics/optional" +) + +// Compose composes a lens with an optional +func Compose[S, A, B any](ab L.Lens[A, B]) func(sa OPT.Optional[S, A]) OPT.Optional[S, B] { + return F.Pipe2( + ab, + LO.LensAsOptional[A, B], + OPT.Compose[S, A, B], + ) +} diff --git a/v2/optics/optional/lens/lens_test.go b/v2/optics/optional/lens/lens_test.go new file mode 100644 index 0000000..ef089fb --- /dev/null +++ b/v2/optics/optional/lens/lens_test.go @@ -0,0 +1,78 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lens + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/optics/lens" + OPT "github.com/IBM/fp-go/v2/optics/optional" + OPTP "github.com/IBM/fp-go/v2/optics/optional/prism" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +type Inner struct { + A int +} + +type State = O.Option[*Inner] + +func (inner *Inner) getA() int { + return inner.A +} + +func (inner *Inner) setA(a int) *Inner { + inner.A = a + return inner +} + +func TestCompose(t *testing.T) { + + inner1 := Inner{1} + + lensa := L.MakeLensRef((*Inner).getA, (*Inner).setA) + + sa := F.Pipe1( + OPT.Id[State](), + OPTP.Some[State, *Inner], + ) + ab := F.Pipe1( + L.IdRef[Inner](), + L.ComposeRef[Inner](lensa), + ) + sb := F.Pipe1( + sa, + Compose[State](ab), + ) + // check get access + assert.Equal(t, O.None[int](), sb.GetOption(O.None[*Inner]())) + assert.Equal(t, O.Of(1), sb.GetOption(O.Of(&inner1))) + + // check set access + res := F.Pipe1( + sb.Set(2)(O.Of(&inner1)), + O.Map(func(i *Inner) int { + return i.A + }), + ) + assert.Equal(t, O.Of(2), res) + assert.Equal(t, 1, inner1.A) + + assert.Equal(t, O.None[*Inner](), sb.Set(2)(O.None[*Inner]())) + +} diff --git a/v2/optics/optional/optional.go b/v2/optics/optional/optional.go new file mode 100644 index 0000000..7ef7601 --- /dev/null +++ b/v2/optics/optional/optional.go @@ -0,0 +1,191 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Optional is an optic used to zoom inside a product. Unlike the `Lens`, the element that the `Optional` focuses +// on may not exist. +package optional + +import ( + EM "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +// Optional is an optional reference to a subpart of a data type +type Optional[S, A any] struct { + GetOption func(s S) O.Option[A] + Set func(a A) EM.Endomorphism[S] +} + +// setCopy wraps a setter for a pointer into a setter that first creates a copy before +// modifying that copy +func setCopy[SET ~func(*S, A) *S, S, A any](setter SET) func(s *S, a A) *S { + return func(s *S, a A) *S { + cpy := *s + return setter(&cpy, a) + } +} + +// MakeOptional creates an Optional based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the +// data. This happens automatically if the data is passed by value. For pointers consider to use `MakeOptionalRef` +// and for other kinds of data structures that are copied by reference make sure the setter creates the copy. +func MakeOptional[S, A any](get func(S) O.Option[A], set func(S, A) S) Optional[S, A] { + return Optional[S, A]{GetOption: get, Set: EM.Curry2(F.Swap(set))} +} + +// MakeOptionalRef creates an Optional based on a getter and a setter function. The setter passed in does not have to create a shallow +// copy, the implementation wraps the setter into one that copies the pointer before modifying it +func MakeOptionalRef[S, A any](get func(*S) O.Option[A], set func(*S, A) *S) Optional[*S, A] { + return MakeOptional(get, setCopy(set)) +} + +// Id returns am optional implementing the identity operation +func id[S any](creator func(get func(S) O.Option[S], set func(S, S) S) Optional[S, S]) Optional[S, S] { + return creator(O.Some[S], F.Second[S, S]) +} + +// Id returns am optional implementing the identity operation +func Id[S any]() Optional[S, S] { + return id(MakeOptional[S, S]) +} + +// Id returns am optional implementing the identity operation +func IdRef[S any]() Optional[*S, *S] { + return id(MakeOptionalRef[S, *S]) +} + +func optionalModifyOption[S, A any](f func(A) A, optional Optional[S, A], s S) O.Option[S] { + return F.Pipe1( + optional.GetOption(s), + O.Map(func(a A) S { + return optional.Set(f(a))(s) + }), + ) +} + +func optionalModify[S, A any](f func(A) A, optional Optional[S, A], s S) S { + return F.Pipe1( + optionalModifyOption(f, optional, s), + O.GetOrElse(F.Constant(s)), + ) +} + +// Compose combines two Optional and allows to narrow down the focus to a sub-Optional +func compose[S, A, B any](creator func(get func(S) O.Option[B], set func(S, B) S) Optional[S, B], ab Optional[A, B]) func(Optional[S, A]) Optional[S, B] { + abget := ab.GetOption + abset := ab.Set + return func(sa Optional[S, A]) Optional[S, B] { + saget := sa.GetOption + return creator( + F.Flow2(saget, O.Chain(abget)), + func(s S, b B) S { + return optionalModify(abset(b), sa, s) + }, + ) + } +} + +// Compose combines two Optional and allows to narrow down the focus to a sub-Optional +func Compose[S, A, B any](ab Optional[A, B]) func(Optional[S, A]) Optional[S, B] { + return compose(MakeOptional[S, B], ab) +} + +// ComposeRef combines two Optional and allows to narrow down the focus to a sub-Optional +func ComposeRef[S, A, B any](ab Optional[A, B]) func(Optional[*S, A]) Optional[*S, B] { + return compose(MakeOptionalRef[S, B], ab) +} + +// fromPredicate implements the function generically for both the ref and the direct case +func fromPredicate[S, A any](creator func(get func(S) O.Option[A], set func(S, A) S) Optional[S, A], pred func(A) bool) func(func(S) A, func(S, A) S) Optional[S, A] { + fromPred := O.FromPredicate(pred) + return func(get func(S) A, set func(S, A) S) Optional[S, A] { + return creator( + F.Flow2(get, fromPred), + func(s S, _ A) S { + return F.Pipe3( + s, + get, + fromPred, + O.Fold(F.Constant(s), F.Bind1st(set, s)), + ) + }, + ) + } +} + +// FromPredicate creates an optional from getter and setter functions. It checks +// for optional values and the correct update procedure +func FromPredicate[S, A any](pred func(A) bool) func(func(S) A, func(S, A) S) Optional[S, A] { + return fromPredicate(MakeOptional[S, A], pred) +} + +// FromPredicate creates an optional from getter and setter functions. It checks +// for optional values and the correct update procedure +func FromPredicateRef[S, A any](pred func(A) bool) func(func(*S) A, func(*S, A) *S) Optional[*S, A] { + return fromPredicate(MakeOptionalRef[S, A], pred) +} + +func imap[S, A, B any](sa Optional[S, A], ab func(A) B, ba func(B) A) Optional[S, B] { + return MakeOptional( + F.Flow2(sa.GetOption, O.Map(ab)), + func(s S, b B) S { + return sa.Set(ba(b))(s) + }, + ) +} + +// IMap implements a bidirectional mapping of the transform +func IMap[S, A, B any](ab func(A) B, ba func(B) A) func(Optional[S, A]) Optional[S, B] { + return func(sa Optional[S, A]) Optional[S, B] { + return imap(sa, ab, ba) + } +} + +func ModifyOption[S, A any](f func(A) A) func(Optional[S, A]) func(S) O.Option[S] { + return func(o Optional[S, A]) func(S) O.Option[S] { + return func(s S) O.Option[S] { + return optionalModifyOption(f, o, s) + } + } +} + +func SetOption[S, A any](a A) func(Optional[S, A]) func(S) O.Option[S] { + return ModifyOption[S](F.Constant1[A](a)) +} + +func ichain[S, A, B any](sa Optional[S, A], ab func(A) O.Option[B], ba func(B) O.Option[A]) Optional[S, B] { + return MakeOptional( + F.Flow2(sa.GetOption, O.Chain(ab)), + func(s S, b B) S { + return O.MonadFold(ba(b), EM.Identity[S], sa.Set)(s) + }, + ) +} + +// IChain implements a bidirectional mapping of the transform if the transform can produce optionals (e.g. in case of type mappings) +func IChain[S, A, B any](ab func(A) O.Option[B], ba func(B) O.Option[A]) func(Optional[S, A]) Optional[S, B] { + return func(sa Optional[S, A]) Optional[S, B] { + return ichain(sa, ab, ba) + } +} + +// IChainAny implements a bidirectional mapping to and from any +func IChainAny[S, A any]() func(Optional[S, any]) Optional[S, A] { + fromAny := O.ToType[A] + toAny := O.ToAny[A] + return func(sa Optional[S, any]) Optional[S, A] { + return ichain(sa, fromAny, toAny) + } +} diff --git a/v2/optics/optional/optional_test.go b/v2/optics/optional/optional_test.go new file mode 100644 index 0000000..892ebe3 --- /dev/null +++ b/v2/optics/optional/optional_test.go @@ -0,0 +1,64 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package optional + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + + "github.com/stretchr/testify/assert" +) + +type ( + Phone struct { + number string + } + + Employment struct { + phone *Phone + } + + Info struct { + employment *Employment + } + + Response struct { + info *Info + } +) + +func (response *Response) GetInfo() *Info { + return response.info +} + +func (response *Response) SetInfo(info *Info) *Response { + response.info = info + return response +} + +var ( + responseOptional = FromPredicateRef[Response](F.IsNonNil[Info])((*Response).GetInfo, (*Response).SetInfo) + + sampleResponse = Response{info: &Info{}} + sampleEmptyResponse = Response{} +) + +func TestOptional(t *testing.T) { + assert.Equal(t, O.Of(sampleResponse.info), responseOptional.GetOption(&sampleResponse)) + assert.Equal(t, O.None[*Info](), responseOptional.GetOption(&sampleEmptyResponse)) +} diff --git a/v2/optics/optional/prism/prism.go b/v2/optics/optional/prism/prism.go new file mode 100644 index 0000000..b450b71 --- /dev/null +++ b/v2/optics/optional/prism/prism.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prism + +import ( + F "github.com/IBM/fp-go/v2/function" + OPT "github.com/IBM/fp-go/v2/optics/optional" + P "github.com/IBM/fp-go/v2/optics/prism" + O "github.com/IBM/fp-go/v2/option" +) + +// AsOptional converts a prism into an optional +func AsOptional[S, A any](sa P.Prism[S, A]) OPT.Optional[S, A] { + return OPT.MakeOptional( + sa.GetOption, + func(s S, a A) S { + return P.Set[S](a)(sa)(s) + }, + ) +} + +func PrismSome[A any]() P.Prism[O.Option[A], A] { + return P.MakePrism(F.Identity[O.Option[A]], O.Some[A]) +} + +// Some returns a `Optional` from a `Optional` focused on the `Some` of a `Option` type. +func Some[S, A any](soa OPT.Optional[S, O.Option[A]]) OPT.Optional[S, A] { + return OPT.Compose[S](AsOptional(PrismSome[A]()))(soa) +} diff --git a/v2/optics/optional/record/generic/generic.go b/v2/optics/optional/record/generic/generic.go new file mode 100644 index 0000000..a661bbf --- /dev/null +++ b/v2/optics/optional/record/generic/generic.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + OP "github.com/IBM/fp-go/v2/optics/optional" + O "github.com/IBM/fp-go/v2/option" + RR "github.com/IBM/fp-go/v2/record/generic" +) + +func setter[M ~map[K]V, K comparable, V any](key K) func(M, V) M { + return func(dst M, value V) M { + return RR.UpsertAt[M](key, value)(dst) + } +} + +func getter[M ~map[K]V, K comparable, V any](key K) func(M) O.Option[V] { + return RR.Lookup[M](key) +} + +// AtKey returns a Optional that gets and sets properties of a map +func AtKey[M ~map[K]V, K comparable, V any](key K) OP.Optional[M, V] { + return OP.MakeOptional(getter[M](key), setter[M](key)) +} diff --git a/v2/optics/optional/record/record.go b/v2/optics/optional/record/record.go new file mode 100644 index 0000000..a65283b --- /dev/null +++ b/v2/optics/optional/record/record.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + OP "github.com/IBM/fp-go/v2/optics/optional" + G "github.com/IBM/fp-go/v2/optics/optional/record/generic" +) + +// FromProperty returns a Optional that gets and sets properties of a map +func AtKey[K comparable, V any](key K) OP.Optional[map[K]V, V] { + return G.AtKey[map[K]V](key) +} diff --git a/v2/optics/optional/record/record_test.go b/v2/optics/optional/record/record_test.go new file mode 100644 index 0000000..4bc3907 --- /dev/null +++ b/v2/optics/optional/record/record_test.go @@ -0,0 +1,100 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + OP "github.com/IBM/fp-go/v2/optics/optional" + O "github.com/IBM/fp-go/v2/option" + ON "github.com/IBM/fp-go/v2/option/number" + RR "github.com/IBM/fp-go/v2/record" + + "github.com/stretchr/testify/assert" +) + +type ( + GenericMap = map[string]any +) + +func TestOptionalRecord(t *testing.T) { + // sample record + r := RR.Singleton("key", "value") + + // extract values + optKey := AtKey[string, string]("key") + optKey1 := AtKey[string, string]("key1") + + // check if we can get the key + assert.Equal(t, O.Of("value"), optKey.GetOption(r)) + assert.Equal(t, O.None[string](), optKey1.GetOption(r)) + + // check if we can set a value + r1 := optKey1.Set("value1")(r) + + // check if we can get the key + assert.Equal(t, O.Of("value"), optKey.GetOption(r)) + assert.Equal(t, O.None[string](), optKey1.GetOption(r)) + // check if we can get the key + assert.Equal(t, O.Of("value"), optKey.GetOption(r1)) + assert.Equal(t, O.Of("value1"), optKey1.GetOption(r1)) +} + +func TestOptionalWithType(t *testing.T) { + // sample record + r := RR.Singleton("key", "1") + // convert between string and int + // writes a key + optStringKey := AtKey[string, string]("key") + optIntKey := F.Pipe1( + optStringKey, + OP.IChain[map[string]string](ON.Atoi, ON.Itoa), + ) + // test the scenarions + assert.Equal(t, O.Of("1"), optStringKey.GetOption(r)) + assert.Equal(t, O.Of(1), optIntKey.GetOption(r)) + // modify + r1 := optIntKey.Set(2)(r) + assert.Equal(t, O.Of("2"), optStringKey.GetOption(r1)) + assert.Equal(t, O.Of(2), optIntKey.GetOption(r1)) +} + +// func TestNestedRecord(t *testing.T) { +// // some sample data +// x := GenericMap{ +// "a": GenericMap{ +// "b": "1", +// }, +// } +// // accessor for first level +// optA := F.Pipe1( +// AtKey[string, any]("a"), +// OP.IChainAny[GenericMap, GenericMap](), +// ) +// optB := F.Pipe2( +// AtKey[string, any]("b"), +// OP.IChainAny[GenericMap, string](), +// OP.IChain[GenericMap](ON.Atoi, ON.Itoa), +// ) +// // go directly to b +// optAB := F.Pipe1( +// optA, +// OP.Compose[GenericMap](optB), +// ) +// // access the value of b +// assert.Equal(t, O.Of(1), optAB.GetOption(x)) +// } diff --git a/v2/optics/prism/prism.go b/v2/optics/prism/prism.go new file mode 100644 index 0000000..1ba68db --- /dev/null +++ b/v2/optics/prism/prism.go @@ -0,0 +1,118 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Prism is an optic used to select part of a sum type. +package prism + +import ( + EM "github.com/IBM/fp-go/v2/endomorphism" + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +type ( + // Prism is an optic used to select part of a sum type. + Prism[S, A any] interface { + GetOption(s S) O.Option[A] + ReverseGet(a A) S + } + + prismImpl[S, A any] struct { + get func(S) O.Option[A] + rev func(A) S + } +) + +func (prism prismImpl[S, A]) GetOption(s S) O.Option[A] { + return prism.get(s) +} + +func (prism prismImpl[S, A]) ReverseGet(a A) S { + return prism.rev(a) +} + +func MakePrism[S, A any](get func(S) O.Option[A], rev func(A) S) Prism[S, A] { + return prismImpl[S, A]{get, rev} +} + +// Id returns a prism implementing the identity operation +func Id[S any]() Prism[S, S] { + return MakePrism(O.Some[S], F.Identity[S]) +} + +func FromPredicate[S any](pred func(S) bool) Prism[S, S] { + return MakePrism(O.FromPredicate(pred), F.Identity[S]) +} + +// Compose composes a `Prism` with a `Prism`. +func Compose[S, A, B any](ab Prism[A, B]) func(Prism[S, A]) Prism[S, B] { + return func(sa Prism[S, A]) Prism[S, B] { + return MakePrism(F.Flow2( + sa.GetOption, + O.Chain(ab.GetOption), + ), F.Flow2( + ab.ReverseGet, + sa.ReverseGet, + )) + } +} + +func prismModifyOption[S, A any](f func(A) A, sa Prism[S, A], s S) O.Option[S] { + return F.Pipe2( + s, + sa.GetOption, + O.Map(F.Flow2( + f, + sa.ReverseGet, + )), + ) +} + +func prismModify[S, A any](f func(A) A, sa Prism[S, A], s S) S { + return F.Pipe1( + prismModifyOption(f, sa, s), + O.GetOrElse(F.Constant(s)), + ) +} + +func prismSet[S, A any](a A) func(Prism[S, A]) EM.Endomorphism[S] { + return EM.Curry3(prismModify[S, A])(F.Constant1[A](a)) +} + +func Set[S, A any](a A) func(Prism[S, A]) EM.Endomorphism[S] { + return EM.Curry3(prismModify[S, A])(F.Constant1[A](a)) +} + +func prismSome[A any]() Prism[O.Option[A], A] { + return MakePrism(F.Identity[O.Option[A]], O.Some[A]) +} + +// Some returns a `Prism` from a `Prism` focused on the `Some` of a `Option` type. +func Some[S, A any](soa Prism[S, O.Option[A]]) Prism[S, A] { + return Compose[S](prismSome[A]())(soa) +} + +func imap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](sa Prism[S, A], ab AB, ba BA) Prism[S, B] { + return MakePrism( + F.Flow2(sa.GetOption, O.Map(ab)), + F.Flow2(ba, sa.ReverseGet), + ) +} + +func IMap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) func(Prism[S, A]) Prism[S, B] { + return func(sa Prism[S, A]) Prism[S, B] { + return imap(sa, ab, ba) + } +} diff --git a/v2/optics/prism/prism_test.go b/v2/optics/prism/prism_test.go new file mode 100644 index 0000000..6fd375b --- /dev/null +++ b/v2/optics/prism/prism_test.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prism + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +func TestSome(t *testing.T) { + somePrism := MakePrism(F.Identity[O.Option[int]], O.Some[int]) + + assert.Equal(t, O.Some(1), somePrism.GetOption(O.Some(1))) + +} diff --git a/v2/optics/prism/traversal.go b/v2/optics/prism/traversal.go new file mode 100644 index 0000000..26e1ad3 --- /dev/null +++ b/v2/optics/prism/traversal.go @@ -0,0 +1,46 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prism + +import ( + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +// AsTraversal converts a prism to a traversal +func AsTraversal[R ~func(func(A) HKTA) func(S) HKTS, S, A, HKTS, HKTA any]( + fof func(S) HKTS, + fmap func(HKTA, func(A) S) HKTS, +) func(Prism[S, A]) R { + return func(sa Prism[S, A]) R { + return func(f func(a A) HKTA) func(S) HKTS { + return func(s S) HKTS { + return F.Pipe2( + s, + sa.GetOption, + O.Fold( + F.Nullary2(F.Constant(s), fof), + func(a A) HKTS { + return fmap(f(a), func(a A) S { + return prismModify(F.Constant1[A](a), sa, s) + }) + }, + ), + ) + } + } + } +} diff --git a/v2/optics/traversal/array/array.go b/v2/optics/traversal/array/array.go new file mode 100644 index 0000000..74de377 --- /dev/null +++ b/v2/optics/traversal/array/array.go @@ -0,0 +1,16 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package array diff --git a/v2/optics/traversal/array/const/traversal.go b/v2/optics/traversal/array/const/traversal.go new file mode 100644 index 0000000..139d957 --- /dev/null +++ b/v2/optics/traversal/array/const/traversal.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package identity + +import ( + C "github.com/IBM/fp-go/v2/constant" + M "github.com/IBM/fp-go/v2/monoid" + AR "github.com/IBM/fp-go/v2/optics/traversal/array/generic/const" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" +) + +// FromArray returns a traversal from an array for the identity [Monoid] +func FromArray[E, A any](m M.Monoid[E]) G.Traversal[[]A, A, C.Const[E, []A], C.Const[E, A]] { + return AR.FromArray[[]A, E, A](m) +} diff --git a/v2/optics/traversal/array/generic/const/traversal.go b/v2/optics/traversal/array/generic/const/traversal.go new file mode 100644 index 0000000..8736a4f --- /dev/null +++ b/v2/optics/traversal/array/generic/const/traversal.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + C "github.com/IBM/fp-go/v2/constant" + M "github.com/IBM/fp-go/v2/monoid" + AR "github.com/IBM/fp-go/v2/optics/traversal/array/generic" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" +) + +// FromArray returns a traversal from an array for the const monad +func FromArray[GA ~[]A, E, A any](m M.Monoid[E]) G.Traversal[GA, A, C.Const[E, GA], C.Const[E, A]] { + return AR.FromArray[GA, GA, A, A, C.Const[E, A], C.Const[E, func(A) GA], C.Const[E, GA]]( + C.Of[E, GA](m), + C.Map[E, GA, func(A) GA], + C.Ap[E, A, GA](m), + ) +} diff --git a/v2/optics/traversal/array/generic/identity/traversal.go b/v2/optics/traversal/array/generic/identity/traversal.go new file mode 100644 index 0000000..8a6f6b0 --- /dev/null +++ b/v2/optics/traversal/array/generic/identity/traversal.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + I "github.com/IBM/fp-go/v2/identity" + AR "github.com/IBM/fp-go/v2/optics/traversal/array/generic" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" +) + +// FromArray returns a traversal from an array for the identity monad +func FromArray[GA ~[]A, A any]() G.Traversal[GA, A, GA, A] { + return AR.FromArray[GA, GA, A, A, A, func(A) GA, GA]( + I.Of[GA], + I.Map[GA, func(A) GA], + I.Ap[GA, A], + ) +} diff --git a/v2/optics/traversal/array/generic/traversal.go b/v2/optics/traversal/array/generic/traversal.go new file mode 100644 index 0000000..47faefa --- /dev/null +++ b/v2/optics/traversal/array/generic/traversal.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + AR "github.com/IBM/fp-go/v2/internal/array" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" +) + +// FromArray returns a traversal from an array +func FromArray[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any]( + fof func(GB) HKTRB, + fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, +) G.Traversal[GA, A, HKTRB, HKTB] { + return func(f func(A) HKTB) func(s GA) HKTRB { + return func(s GA) HKTRB { + return AR.MonadTraverse(fof, fmap, fap, s, f) + } + } +} diff --git a/v2/optics/traversal/array/identity/traversal.go b/v2/optics/traversal/array/identity/traversal.go new file mode 100644 index 0000000..2b209e4 --- /dev/null +++ b/v2/optics/traversal/array/identity/traversal.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package identity + +import ( + AR "github.com/IBM/fp-go/v2/optics/traversal/array/generic/identity" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" +) + +// FromArray returns a traversal from an array for the identity monad +func FromArray[A any]() G.Traversal[[]A, A, []A, A] { + return AR.FromArray[[]A, A]() +} diff --git a/v2/optics/traversal/either/traversal.go b/v2/optics/traversal/either/traversal.go new file mode 100644 index 0000000..a348d91 --- /dev/null +++ b/v2/optics/traversal/either/traversal.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + ET "github.com/IBM/fp-go/v2/either" + T "github.com/IBM/fp-go/v2/optics/traversal/generic" +) + +type ( + Traversal[E, S, A any] T.Traversal[S, A, ET.Either[E, S], ET.Either[E, A]] +) + +func Compose[ + E, S, A, B any](ab Traversal[E, A, B]) func(Traversal[E, S, A]) Traversal[E, S, B] { + return T.Compose[ + Traversal[E, A, B], + Traversal[E, S, A], + Traversal[E, S, B], + ](ab) +} diff --git a/v2/optics/traversal/generic/traversal.go b/v2/optics/traversal/generic/traversal.go new file mode 100644 index 0000000..1fc76f9 --- /dev/null +++ b/v2/optics/traversal/generic/traversal.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + AR "github.com/IBM/fp-go/v2/array/generic" + C "github.com/IBM/fp-go/v2/constant" + F "github.com/IBM/fp-go/v2/function" +) + +type ( + Traversal[S, A, HKTS, HKTA any] func(func(A) HKTA) func(S) HKTS +) + +func Compose[ + TAB ~func(func(B) HKTB) func(A) HKTA, + TSA ~func(func(A) HKTA) func(S) HKTS, + TSB ~func(func(B) HKTB) func(S) HKTS, + S, A, B, HKTS, HKTA, HKTB any](ab TAB) func(TSA) TSB { + return func(sa TSA) TSB { + return F.Flow2(ab, sa) + } +} + +func FromTraversable[ + TAB ~func(func(A) HKTFA) func(HKTTA) HKTAA, + A, + HKTTA, + HKTFA, + HKTAA any]( + traverseF func(HKTTA, func(A) HKTFA) HKTAA, +) TAB { + return F.Bind1st(F.Bind2nd[HKTTA, func(A) HKTFA, HKTAA], traverseF) +} + +// FoldMap maps each target to a `Monoid` and combines the result +func FoldMap[M, S, A any](f func(A) M) func(sa Traversal[S, A, C.Const[M, S], C.Const[M, A]]) func(S) M { + return func(sa Traversal[S, A, C.Const[M, S], C.Const[M, A]]) func(S) M { + return F.Flow2( + F.Pipe1( + F.Flow2(f, C.Make[M, A]), + sa, + ), + C.Unwrap[M, S], + ) + } +} + +// Fold maps each target to a `Monoid` and combines the result +func Fold[S, A any](sa Traversal[S, A, C.Const[A, S], C.Const[A, A]]) func(S) A { + return FoldMap[A, S, A](F.Identity[A])(sa) +} + +// GetAll gets all the targets of a traversal +func GetAll[GA ~[]A, S, A any](s S) func(sa Traversal[S, A, C.Const[GA, S], C.Const[GA, A]]) GA { + fmap := FoldMap[GA, S, A](AR.Of[GA, A]) + return func(sa Traversal[S, A, C.Const[GA, S], C.Const[GA, A]]) GA { + return fmap(sa)(s) + } +} diff --git a/v2/optics/traversal/option/traversal.go b/v2/optics/traversal/option/traversal.go new file mode 100644 index 0000000..0ee1a8e --- /dev/null +++ b/v2/optics/traversal/option/traversal.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + T "github.com/IBM/fp-go/v2/optics/traversal/generic" + O "github.com/IBM/fp-go/v2/option" +) + +type ( + Traversal[S, A any] T.Traversal[S, A, O.Option[S], O.Option[A]] +) + +func Compose[ + S, A, B any](ab Traversal[A, B]) func(Traversal[S, A]) Traversal[S, B] { + return T.Compose[ + Traversal[A, B], + Traversal[S, A], + Traversal[S, B], + ](ab) +} diff --git a/v2/optics/traversal/record/const/traversal.go b/v2/optics/traversal/record/const/traversal.go new file mode 100644 index 0000000..cb8943d --- /dev/null +++ b/v2/optics/traversal/record/const/traversal.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + C "github.com/IBM/fp-go/v2/constant" + M "github.com/IBM/fp-go/v2/monoid" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" + RR "github.com/IBM/fp-go/v2/optics/traversal/record/generic/const" +) + +// FromRecord returns a traversal from an array for the const monad +func FromRecord[E, K comparable, A any](m M.Monoid[E]) G.Traversal[map[K]A, A, C.Const[E, map[K]A], C.Const[E, A]] { + return RR.FromRecord[map[K]A, E, K, A](m) +} diff --git a/v2/optics/traversal/record/generic/const/traversal.go b/v2/optics/traversal/record/generic/const/traversal.go new file mode 100644 index 0000000..e3d939b --- /dev/null +++ b/v2/optics/traversal/record/generic/const/traversal.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + C "github.com/IBM/fp-go/v2/constant" + M "github.com/IBM/fp-go/v2/monoid" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" + RR "github.com/IBM/fp-go/v2/optics/traversal/record/generic" +) + +// FromRecord returns a traversal from an array for the const monad +func FromRecord[MA ~map[K]A, E, K comparable, A any](m M.Monoid[E]) G.Traversal[MA, A, C.Const[E, MA], C.Const[E, A]] { + return RR.FromRecord[MA, MA, K, A, A, C.Const[E, A], C.Const[E, func(A) MA], C.Const[E, MA]]( + C.Of[E, MA](m), + C.Map[E, MA, func(A) MA], + C.Ap[E, A, MA](m), + ) +} diff --git a/v2/optics/traversal/record/generic/identity/traversal.go b/v2/optics/traversal/record/generic/identity/traversal.go new file mode 100644 index 0000000..232e7be --- /dev/null +++ b/v2/optics/traversal/record/generic/identity/traversal.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + I "github.com/IBM/fp-go/v2/identity" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" + RR "github.com/IBM/fp-go/v2/optics/traversal/record/generic" +) + +// FromRecord returns a traversal from a record for the identity monad +func FromRecord[MA ~map[K]A, K comparable, A any]() G.Traversal[MA, A, MA, A] { + return RR.FromRecord[MA, MA, K, A, A, A, func(A) MA, MA]( + I.Of[MA], + I.Map[MA, func(A) MA], + I.Ap[MA, A], + ) +} diff --git a/v2/optics/traversal/record/generic/traversal.go b/v2/optics/traversal/record/generic/traversal.go new file mode 100644 index 0000000..e59aa8d --- /dev/null +++ b/v2/optics/traversal/record/generic/traversal.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + R "github.com/IBM/fp-go/v2/internal/record" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" +) + +// FromRecord returns a traversal from a record +func FromRecord[MA ~map[K]A, MB ~map[K]B, K comparable, A, B, HKTB, HKTAB, HKTRB any]( + fof func(MB) HKTRB, + fmap func(func(MB) func(B) MB) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, +) G.Traversal[MA, A, HKTRB, HKTB] { + return func(f func(A) HKTB) func(s MA) HKTRB { + return func(s MA) HKTRB { + return R.MonadTraverse(fof, fmap, fap, s, f) + } + } +} diff --git a/v2/optics/traversal/record/identity/traversal.go b/v2/optics/traversal/record/identity/traversal.go new file mode 100644 index 0000000..d5c2c4d --- /dev/null +++ b/v2/optics/traversal/record/identity/traversal.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package identity + +import ( + G "github.com/IBM/fp-go/v2/optics/traversal/generic" + RR "github.com/IBM/fp-go/v2/optics/traversal/record/generic/identity" +) + +// FromRecord returns a traversal from an array for the identity monad +func FromRecord[K comparable, A any]() G.Traversal[map[K]A, A, map[K]A, A] { + return RR.FromRecord[map[K]A, K, A]() +} diff --git a/v2/optics/traversal/record/traversal.go b/v2/optics/traversal/record/traversal.go new file mode 100644 index 0000000..7cfd8bd --- /dev/null +++ b/v2/optics/traversal/record/traversal.go @@ -0,0 +1,16 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record diff --git a/v2/optics/traversal/traversal.go b/v2/optics/traversal/traversal.go new file mode 100644 index 0000000..5f7120d --- /dev/null +++ b/v2/optics/traversal/traversal.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package traversal + +import ( + C "github.com/IBM/fp-go/v2/constant" + F "github.com/IBM/fp-go/v2/function" + G "github.com/IBM/fp-go/v2/optics/traversal/generic" +) + +// Id is the identity constructor of a traversal +func Id[S, A any]() G.Traversal[S, S, A, A] { + return F.Identity[func(S) A] +} + +// Modify applies a transformation function to a traversal +func Modify[S, A any](f func(A) A) func(sa G.Traversal[S, A, S, A]) func(S) S { + return func(sa G.Traversal[S, A, S, A]) func(S) S { + return sa(f) + } +} + +// Set sets a constant value for all values of the traversal +func Set[S, A any](a A) func(sa G.Traversal[S, A, S, A]) func(S) S { + return Modify[S, A](F.Constant1[A](a)) +} + +// FoldMap maps each target to a `Monoid` and combines the result +func FoldMap[M, S, A any](f func(A) M) func(sa G.Traversal[S, A, C.Const[M, S], C.Const[M, A]]) func(S) M { + return G.FoldMap[M, S, A](f) +} + +// Fold maps each target to a `Monoid` and combines the result +func Fold[S, A any](sa G.Traversal[S, A, C.Const[A, S], C.Const[A, A]]) func(S) A { + return G.Fold[S, A](sa) +} + +// GetAll gets all the targets of a traversal +func GetAll[S, A any](s S) func(sa G.Traversal[S, A, C.Const[[]A, S], C.Const[[]A, A]]) []A { + return G.GetAll[[]A, S, A](s) +} + +// Compose composes two traversables +func Compose[ + S, A, B, HKTS, HKTA, HKTB any](ab G.Traversal[A, B, HKTA, HKTB]) func(sa G.Traversal[S, A, HKTS, HKTA]) G.Traversal[S, B, HKTS, HKTB] { + return G.Compose[ + G.Traversal[A, B, HKTA, HKTB], + G.Traversal[S, A, HKTS, HKTA], + G.Traversal[S, B, HKTS, HKTB], + S, A, B, + HKTS, HKTA, HKTB, + ](ab) +} diff --git a/v2/optics/traversal/traversal_test.go b/v2/optics/traversal/traversal_test.go new file mode 100644 index 0000000..1316d78 --- /dev/null +++ b/v2/optics/traversal/traversal_test.go @@ -0,0 +1,79 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package traversal + +import ( + "testing" + + AR "github.com/IBM/fp-go/v2/array" + C "github.com/IBM/fp-go/v2/constant" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + N "github.com/IBM/fp-go/v2/number" + AT "github.com/IBM/fp-go/v2/optics/traversal/array/const" + AI "github.com/IBM/fp-go/v2/optics/traversal/array/identity" + "github.com/stretchr/testify/assert" +) + +func TestGetAll(t *testing.T) { + + as := AR.From(1, 2, 3) + + tr := AT.FromArray[[]int, int](AR.Monoid[int]()) + + sa := F.Pipe1( + Id[[]int, C.Const[[]int, []int]](), + Compose[[]int, []int, int, C.Const[[]int, []int]](tr), + ) + + getall := GetAll[[]int, int](as)(sa) + + assert.Equal(t, AR.From(1, 2, 3), getall) +} + +func TestFold(t *testing.T) { + + monoidSum := N.MonoidSum[int]() + + as := AR.From(1, 2, 3) + + tr := AT.FromArray[int, int](monoidSum) + + sa := F.Pipe1( + Id[[]int, C.Const[int, []int]](), + Compose[[]int, []int, int, C.Const[int, []int]](tr), + ) + + folded := Fold[[]int, int](sa)(as) + + assert.Equal(t, 6, folded) +} + +func TestTraverse(t *testing.T) { + + as := AR.From(1, 2, 3) + + tr := AI.FromArray[int]() + + sa := F.Pipe1( + Id[[]int, []int](), + Compose[[]int, []int, int, []int, []int, int](tr), + ) + + res := sa(utils.Double)(as) + + assert.Equal(t, AR.From(2, 4, 6), res) +} diff --git a/v2/option/apply.go b/v2/option/apply.go new file mode 100644 index 0000000..d754346 --- /dev/null +++ b/v2/option/apply.go @@ -0,0 +1,30 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[Option[A]] { + return S.ApplySemigroup(MonadMap[A, func(A) A], MonadAp[A, A], s) +} + +// ApplicativeMonoid returns a [Monoid] that concatenates [Option] instances via their applicative +func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Option[A]] { + return M.ApplicativeMonoid(Of[A], MonadMap[A, func(A) A], MonadAp[A, A], m) +} diff --git a/v2/option/array.go b/v2/option/array.go new file mode 100644 index 0000000..7d79199 --- /dev/null +++ b/v2/option/array.go @@ -0,0 +1,74 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" +) + +// TraverseArray transforms an array +func TraverseArrayG[GA ~[]A, GB ~[]B, A, B any](f func(A) Option[B]) func(GA) Option[GB] { + return RA.Traverse[GA]( + Of[GB], + Map[GB, func(B) GB], + Ap[GB, B], + + f, + ) +} + +// TraverseArray transforms an array +func TraverseArray[A, B any](f func(A) Option[B]) func([]A) Option[[]B] { + return TraverseArrayG[[]A, []B](f) +} + +// TraverseArrayWithIndexG transforms an array +func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, A, B any](f func(int, A) Option[B]) func(GA) Option[GB] { + return RA.TraverseWithIndex[GA]( + Of[GB], + Map[GB, func(B) GB], + Ap[GB, B], + + f, + ) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[A, B any](f func(int, A) Option[B]) func([]A) Option[[]B] { + return TraverseArrayWithIndexG[[]A, []B](f) +} + +func SequenceArrayG[GA ~[]A, GOA ~[]Option[A], A any](ma GOA) Option[GA] { + return TraverseArrayG[GOA, GA](F.Identity[Option[A]])(ma) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[A any](ma []Option[A]) Option[[]A] { + return SequenceArrayG[[]A](ma) +} + +// CompactArrayG discards the none values and keeps the some values +func CompactArrayG[A1 ~[]Option[A], A2 ~[]A, A any](fa A1) A2 { + return RA.Reduce(fa, func(out A2, value Option[A]) A2 { + return MonadFold(value, F.Constant(out), F.Bind1st(RA.Append[A2, A], out)) + }, make(A2, 0, len(fa))) +} + +// CompactArray discards the none values and keeps the some values +func CompactArray[A any](fa []Option[A]) []A { + return CompactArrayG[[]Option[A], []A](fa) +} diff --git a/v2/option/array_test.go b/v2/option/array_test.go new file mode 100644 index 0000000..a9090f3 --- /dev/null +++ b/v2/option/array_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "fmt" + "testing" + + TST "github.com/IBM/fp-go/v2/internal/testing" + "github.com/stretchr/testify/assert" +) + +func TestCompactArray(t *testing.T) { + ar := []Option[string]{ + Of("ok"), + None[string](), + Of("ok"), + } + + res := CompactArray(ar) + assert.Equal(t, 2, len(res)) +} + +func TestSequenceArray(t *testing.T) { + + s := TST.SequenceArrayTest( + FromStrictEquals[bool](), + Pointed[string](), + Pointed[bool](), + Functor[[]string, bool](), + SequenceArray[string], + ) + + for i := 0; i < 10; i++ { + t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i)) + } +} diff --git a/v2/option/bind.go b/v2/option/bind.go new file mode 100644 index 0000000..444c335 --- /dev/null +++ b/v2/option/bind.go @@ -0,0 +1,89 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[S any]( + empty S, +) Option[S] { + return Of(empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, S2, A any]( + setter func(A) func(S1) S2, + f func(S1) Option[A], +) func(Option[S1]) Option[S2] { + return C.Bind( + Chain[S1, S2], + Map[A, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, S2, B any]( + key func(B) func(S1) S2, + f func(S1) B, +) func(Option[S1]) Option[S2] { + return F.Let( + Map[S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(Option[S1]) Option[S2] { + return F.LetTo( + Map[S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any]( + setter func(T) S1, +) func(Option[T]) Option[S1] { + return C.BindTo( + Map[T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa Option[T], +) func(Option[S1]) Option[S2] { + return A.ApS( + Ap[S2, T], + Map[S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/option/bind_test.go b/v2/option/bind_test.go new file mode 100644 index 0000000..2c37907 --- /dev/null +++ b/v2/option/bind_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) Option[string] { + return Of("Doe") +} + +func getGivenName(s utils.WithLastName) Option[string] { + return Of("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map(utils.GetFullName), + ) + + assert.Equal(t, res, Of("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do(utils.Empty), + ApS(utils.SetLastName, Of("Doe")), + ApS(utils.SetGivenName, Of("John")), + Map(utils.GetFullName), + ) + + assert.Equal(t, res, Of("John Doe")) +} diff --git a/v2/option/core.go b/v2/option/core.go new file mode 100644 index 0000000..8bd72c1 --- /dev/null +++ b/v2/option/core.go @@ -0,0 +1,126 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" +) + +var ( + // jsonNull is the cached representation of the `null` serialization in JSON + jsonNull = []byte("null") +) + +// Option defines a data structure that logically holds a value or not +type Option[A any] struct { + isSome bool + value A +} + +// optString prints some debug info for the object +// +// go:noinline +func optString(isSome bool, value any) string { + if isSome { + return fmt.Sprintf("Some[%T](%v)", value, value) + } + return fmt.Sprintf("None[%T]", value) +} + +// optFormat prints some debug info for the object +// +// go:noinline +func optFormat(isSome bool, value any, f fmt.State, c rune) { + switch c { + case 's': + fmt.Fprint(f, optString(isSome, value)) + default: + fmt.Fprint(f, optString(isSome, value)) + } +} + +// String prints some debug info for the object +func (s Option[A]) String() string { + return optString(s.isSome, s.value) +} + +// Format prints some debug info for the object +func (s Option[A]) Format(f fmt.State, c rune) { + optFormat(s.isSome, s.value, f, c) +} + +func optMarshalJSON(isSome bool, value any) ([]byte, error) { + if isSome { + return json.Marshal(value) + } + return jsonNull, nil +} + +func (s Option[A]) MarshalJSON() ([]byte, error) { + return optMarshalJSON(s.isSome, s.value) +} + +// optUnmarshalJSON unmarshals the [Option] from a JSON string +// +// go:noinline +func optUnmarshalJSON(isSome *bool, value any, data []byte) error { + // decode the value + if bytes.Equal(data, jsonNull) { + *isSome = false + reflect.ValueOf(value).Elem().SetZero() + return nil + } + *isSome = true + return json.Unmarshal(data, value) +} + +func (s *Option[A]) UnmarshalJSON(data []byte) error { + return optUnmarshalJSON(&s.isSome, &s.value, data) +} + +func IsNone[T any](val Option[T]) bool { + return !val.isSome +} + +func Some[T any](value T) Option[T] { + return Option[T]{isSome: true, value: value} +} + +func Of[T any](value T) Option[T] { + return Some(value) +} + +func None[T any]() Option[T] { + return Option[T]{isSome: false} +} + +func IsSome[T any](val Option[T]) bool { + return val.isSome +} + +func MonadFold[A, B any](ma Option[A], onNone func() B, onSome func(A) B) B { + if IsSome(ma) { + return onSome(ma.value) + } + return onNone() +} + +func Unwrap[A any](ma Option[A]) (A, bool) { + return ma.value, ma.isSome +} diff --git a/v2/option/doc.go b/v2/option/doc.go new file mode 100644 index 0000000..49dae03 --- /dev/null +++ b/v2/option/doc.go @@ -0,0 +1,19 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package option defines the [Option] datastructure and its monadic operations +package option + +//go:generate go run .. option --count 10 --filename gen.go diff --git a/v2/option/eq.go b/v2/option/eq.go new file mode 100644 index 0000000..e8d82cd --- /dev/null +++ b/v2/option/eq.go @@ -0,0 +1,37 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" +) + +// Constructs an equal predicate for an `Option` +func Eq[A any](a EQ.Eq[A]) EQ.Eq[Option[A]] { + // some convenient shortcuts + fld := Fold( + F.Constant(Fold(F.ConstTrue, F.Constant1[A](false))), + F.Flow2(F.Curry2(a.Equals), F.Bind1st(Fold[A, bool], F.ConstFalse)), + ) + // convert to an equals predicate + return EQ.FromEquals(F.Uncurry2(fld)) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[A comparable]() EQ.Eq[Option[A]] { + return Eq(EQ.FromStrictEquals[A]()) +} diff --git a/v2/option/eq_test.go b/v2/option/eq_test.go new file mode 100644 index 0000000..1efa8f8 --- /dev/null +++ b/v2/option/eq_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEq(t *testing.T) { + + r1 := Of(1) + r2 := Of(1) + r3 := Of(2) + + n1 := None[int]() + + eq := FromStrictEquals[int]() + + assert.True(t, eq.Equals(r1, r1)) + assert.True(t, eq.Equals(r1, r2)) + assert.False(t, eq.Equals(r1, r3)) + assert.False(t, eq.Equals(r1, n1)) + + assert.True(t, eq.Equals(n1, n1)) + assert.False(t, eq.Equals(n1, r2)) +} diff --git a/v2/option/examples_create_test.go b/v2/option/examples_create_test.go new file mode 100644 index 0000000..c85e3e3 --- /dev/null +++ b/v2/option/examples_create_test.go @@ -0,0 +1,55 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import "fmt" + +func ExampleOption_creation() { + + // Build an Option + none1 := None[int]() + some1 := Some("value") + + // Build from a value + fromNillable := FromNillable[string] + nonFromNil := fromNillable(nil) // None[*string] + value := "value" + someFromPointer := fromNillable(&value) // Some[*string](xxx) + + // some predicate + isEven := func(num int) bool { + return num%2 == 0 + } + + fromEven := FromPredicate(isEven) + noneFromPred := fromEven(3) // None[int] + someFromPred := fromEven(4) // Some[int](4) + + fmt.Println(none1) + fmt.Println(some1) + fmt.Println(nonFromNil) + fmt.Println(IsSome(someFromPointer)) + fmt.Println(noneFromPred) + fmt.Println(someFromPred) + + // Output: + // None[int] + // Some[string](value) + // None[*string] + // true + // None[int] + // Some[int](4) +} diff --git a/v2/option/examples_extract_test.go b/v2/option/examples_extract_test.go new file mode 100644 index 0000000..b69da85 --- /dev/null +++ b/v2/option/examples_extract_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" +) + +func ExampleOption_extraction() { + + noneValue := None[int]() + someValue := Of(42) + + // Convert Option[T] to T + fromNone, okFromNone := Unwrap(noneValue) // 0, false + fromSome, okFromSome := Unwrap(someValue) // 42, true + + // Convert Option[T] with a default value + noneWithDefault := GetOrElse(F.Constant(0))(noneValue) // 0 + someWithDefault := GetOrElse(F.Constant(0))(someValue) // 42 + + // Apply a different function on None/Some(...) + doubleOrZero := Fold( + F.Constant(0), // none case + N.Mul(2), // some case + ) // func(ma Option[int]) int + + doubleFromNone := doubleOrZero(noneValue) // 0 + doubleFromSome := doubleOrZero(someValue) // 84 + + // Pro-tip: Fold is short for the following: + doubleOfZeroBis := F.Flow2( + Map(N.Mul(2)), // some case + GetOrElse(F.Constant(0)), // none case + ) + doubleFromNoneBis := doubleOfZeroBis(noneValue) // 0 + doubleFromSomeBis := doubleOfZeroBis(someValue) // 84 + + fmt.Printf("%d, %t\n", fromNone, okFromNone) + fmt.Printf("%d, %t\n", fromSome, okFromSome) + fmt.Println(noneWithDefault) + fmt.Println(someWithDefault) + fmt.Println(doubleFromNone) + fmt.Println(doubleFromSome) + fmt.Println(doubleFromNoneBis) + fmt.Println(doubleFromSomeBis) + + // Output: + // 0, false + // 42, true + // 0 + // 42 + // 0 + // 84 + // 0 + // 84 +} diff --git a/v2/option/functor.go b/v2/option/functor.go new file mode 100644 index 0000000..550c6f5 --- /dev/null +++ b/v2/option/functor.go @@ -0,0 +1,31 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "github.com/IBM/fp-go/v2/internal/functor" +) + +type optionFunctor[A, B any] struct{} + +func (o *optionFunctor[A, B]) Map(f func(A) B) func(Option[A]) Option[B] { + return Map[A, B](f) +} + +// Functor implements the functoric operations for [Option] +func Functor[A, B any]() functor.Functor[A, B, Option[A], Option[B]] { + return &optionFunctor[A, B]{} +} diff --git a/v2/option/gen.go b/v2/option/gen.go new file mode 100644 index 0000000..feb3f59 --- /dev/null +++ b/v2/option/gen.go @@ -0,0 +1,689 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:19.6336283 +0100 CET m=+0.018011101 + +package option + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// optionize converts a nullary function to an option +func optionize[R any](f func() (R, bool)) Option[R] { + if r, ok := f(); ok { + return Some(r) + } + return None[R]() +} + +// Optionize0 converts a function with 0 parameters returning a tuple of a return value R and a boolean into a function with 0 parameters returning an Option[R] +func Optionize0[F ~func() (R, bool), R any](f F) func() Option[R] { + return func() Option[R] { + return optionize(func() (R, bool) { + return f() + }) + } +} + +// Unoptionize0 converts a function with 0 parameters returning a tuple of a return value R and a boolean into a function with 0 parameters returning an Option[R] +func Unoptionize0[F ~func() Option[R], R any](f F) func() (R, bool) { + return func() (R, bool) { + return Unwrap(f()) + } +} + +// Optionize1 converts a function with 1 parameters returning a tuple of a return value R and a boolean into a function with 1 parameters returning an Option[R] +func Optionize1[F ~func(T0) (R, bool), T0, R any](f F) func(T0) Option[R] { + return func(t0 T0) Option[R] { + return optionize(func() (R, bool) { + return f(t0) + }) + } +} + +// Unoptionize1 converts a function with 1 parameters returning a tuple of a return value R and a boolean into a function with 1 parameters returning an Option[R] +func Unoptionize1[F ~func(T0) Option[R], T0, R any](f F) func(T0) (R, bool) { + return func(t0 T0) (R, bool) { + return Unwrap(f(t0)) + } +} + +// SequenceT1 converts 1 parameters of [Option[T]] into a [Option[Tuple1]]. +func SequenceT1[T1 any](t1 Option[T1]) Option[T.Tuple1[T1]] { + return A.SequenceT1( + Map[T1, T.Tuple1[T1]], + t1, + ) +} + +// SequenceTuple1 converts a [Tuple1] of [Option[T]] into an [Option[Tuple1]]. +func SequenceTuple1[T1 any](t T.Tuple1[Option[T1]]) Option[T.Tuple1[T1]] { + return A.SequenceTuple1( + Map[T1, T.Tuple1[T1]], + t, + ) +} + +// TraverseTuple1 converts a [Tuple1] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple1]]. +func TraverseTuple1[F1 ~func(A1) Option[T1], A1, T1 any](f1 F1) func(T.Tuple1[A1]) Option[T.Tuple1[T1]] { + return func(t T.Tuple1[A1]) Option[T.Tuple1[T1]] { + return A.TraverseTuple1( + Map[T1, T.Tuple1[T1]], + f1, + t, + ) + } +} + +// Optionize2 converts a function with 2 parameters returning a tuple of a return value R and a boolean into a function with 2 parameters returning an Option[R] +func Optionize2[F ~func(T0, T1) (R, bool), T0, T1, R any](f F) func(T0, T1) Option[R] { + return func(t0 T0, t1 T1) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1) + }) + } +} + +// Unoptionize2 converts a function with 2 parameters returning a tuple of a return value R and a boolean into a function with 2 parameters returning an Option[R] +func Unoptionize2[F ~func(T0, T1) Option[R], T0, T1, R any](f F) func(T0, T1) (R, bool) { + return func(t0 T0, t1 T1) (R, bool) { + return Unwrap(f(t0, t1)) + } +} + +// SequenceT2 converts 2 parameters of [Option[T]] into a [Option[Tuple2]]. +func SequenceT2[T1, T2 any](t1 Option[T1], t2 Option[T2]) Option[T.Tuple2[T1, T2]] { + return A.SequenceT2( + Map[T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], T2], + t1, + t2, + ) +} + +// SequenceTuple2 converts a [Tuple2] of [Option[T]] into an [Option[Tuple2]]. +func SequenceTuple2[T1, T2 any](t T.Tuple2[Option[T1], Option[T2]]) Option[T.Tuple2[T1, T2]] { + return A.SequenceTuple2( + Map[T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], T2], + t, + ) +} + +// TraverseTuple2 converts a [Tuple2] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple2]]. +func TraverseTuple2[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], A1, T1, A2, T2 any](f1 F1, f2 F2) func(T.Tuple2[A1, A2]) Option[T.Tuple2[T1, T2]] { + return func(t T.Tuple2[A1, A2]) Option[T.Tuple2[T1, T2]] { + return A.TraverseTuple2( + Map[T1, func(T2) T.Tuple2[T1, T2]], + Ap[T.Tuple2[T1, T2], T2], + f1, + f2, + t, + ) + } +} + +// Optionize3 converts a function with 3 parameters returning a tuple of a return value R and a boolean into a function with 3 parameters returning an Option[R] +func Optionize3[F ~func(T0, T1, T2) (R, bool), T0, T1, T2, R any](f F) func(T0, T1, T2) Option[R] { + return func(t0 T0, t1 T1, t2 T2) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1, t2) + }) + } +} + +// Unoptionize3 converts a function with 3 parameters returning a tuple of a return value R and a boolean into a function with 3 parameters returning an Option[R] +func Unoptionize3[F ~func(T0, T1, T2) Option[R], T0, T1, T2, R any](f F) func(T0, T1, T2) (R, bool) { + return func(t0 T0, t1 T1, t2 T2) (R, bool) { + return Unwrap(f(t0, t1, t2)) + } +} + +// SequenceT3 converts 3 parameters of [Option[T]] into a [Option[Tuple3]]. +func SequenceT3[T1, T2, T3 any](t1 Option[T1], t2 Option[T2], t3 Option[T3]) Option[T.Tuple3[T1, T2, T3]] { + return A.SequenceT3( + Map[T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], T2], + Ap[T.Tuple3[T1, T2, T3], T3], + t1, + t2, + t3, + ) +} + +// SequenceTuple3 converts a [Tuple3] of [Option[T]] into an [Option[Tuple3]]. +func SequenceTuple3[T1, T2, T3 any](t T.Tuple3[Option[T1], Option[T2], Option[T3]]) Option[T.Tuple3[T1, T2, T3]] { + return A.SequenceTuple3( + Map[T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], T2], + Ap[T.Tuple3[T1, T2, T3], T3], + t, + ) +} + +// TraverseTuple3 converts a [Tuple3] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple3]]. +func TraverseTuple3[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], F3 ~func(A3) Option[T3], A1, T1, A2, T2, A3, T3 any](f1 F1, f2 F2, f3 F3) func(T.Tuple3[A1, A2, A3]) Option[T.Tuple3[T1, T2, T3]] { + return func(t T.Tuple3[A1, A2, A3]) Option[T.Tuple3[T1, T2, T3]] { + return A.TraverseTuple3( + Map[T1, func(T2) func(T3) T.Tuple3[T1, T2, T3]], + Ap[func(T3) T.Tuple3[T1, T2, T3], T2], + Ap[T.Tuple3[T1, T2, T3], T3], + f1, + f2, + f3, + t, + ) + } +} + +// Optionize4 converts a function with 4 parameters returning a tuple of a return value R and a boolean into a function with 4 parameters returning an Option[R] +func Optionize4[F ~func(T0, T1, T2, T3) (R, bool), T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) Option[R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1, t2, t3) + }) + } +} + +// Unoptionize4 converts a function with 4 parameters returning a tuple of a return value R and a boolean into a function with 4 parameters returning an Option[R] +func Unoptionize4[F ~func(T0, T1, T2, T3) Option[R], T0, T1, T2, T3, R any](f F) func(T0, T1, T2, T3) (R, bool) { + return func(t0 T0, t1 T1, t2 T2, t3 T3) (R, bool) { + return Unwrap(f(t0, t1, t2, t3)) + } +} + +// SequenceT4 converts 4 parameters of [Option[T]] into a [Option[Tuple4]]. +func SequenceT4[T1, T2, T3, T4 any](t1 Option[T1], t2 Option[T2], t3 Option[T3], t4 Option[T4]) Option[T.Tuple4[T1, T2, T3, T4]] { + return A.SequenceT4( + Map[T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], T3], + Ap[T.Tuple4[T1, T2, T3, T4], T4], + t1, + t2, + t3, + t4, + ) +} + +// SequenceTuple4 converts a [Tuple4] of [Option[T]] into an [Option[Tuple4]]. +func SequenceTuple4[T1, T2, T3, T4 any](t T.Tuple4[Option[T1], Option[T2], Option[T3], Option[T4]]) Option[T.Tuple4[T1, T2, T3, T4]] { + return A.SequenceTuple4( + Map[T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], T3], + Ap[T.Tuple4[T1, T2, T3, T4], T4], + t, + ) +} + +// TraverseTuple4 converts a [Tuple4] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple4]]. +func TraverseTuple4[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], F3 ~func(A3) Option[T3], F4 ~func(A4) Option[T4], A1, T1, A2, T2, A3, T3, A4, T4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(T.Tuple4[A1, A2, A3, A4]) Option[T.Tuple4[T1, T2, T3, T4]] { + return func(t T.Tuple4[A1, A2, A3, A4]) Option[T.Tuple4[T1, T2, T3, T4]] { + return A.TraverseTuple4( + Map[T1, func(T2) func(T3) func(T4) T.Tuple4[T1, T2, T3, T4]], + Ap[func(T3) func(T4) T.Tuple4[T1, T2, T3, T4], T2], + Ap[func(T4) T.Tuple4[T1, T2, T3, T4], T3], + Ap[T.Tuple4[T1, T2, T3, T4], T4], + f1, + f2, + f3, + f4, + t, + ) + } +} + +// Optionize5 converts a function with 5 parameters returning a tuple of a return value R and a boolean into a function with 5 parameters returning an Option[R] +func Optionize5[F ~func(T0, T1, T2, T3, T4) (R, bool), T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) Option[R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1, t2, t3, t4) + }) + } +} + +// Unoptionize5 converts a function with 5 parameters returning a tuple of a return value R and a boolean into a function with 5 parameters returning an Option[R] +func Unoptionize5[F ~func(T0, T1, T2, T3, T4) Option[R], T0, T1, T2, T3, T4, R any](f F) func(T0, T1, T2, T3, T4) (R, bool) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) (R, bool) { + return Unwrap(f(t0, t1, t2, t3, t4)) + } +} + +// SequenceT5 converts 5 parameters of [Option[T]] into a [Option[Tuple5]]. +func SequenceT5[T1, T2, T3, T4, T5 any](t1 Option[T1], t2 Option[T2], t3 Option[T3], t4 Option[T4], t5 Option[T5]) Option[T.Tuple5[T1, T2, T3, T4, T5]] { + return A.SequenceT5( + Map[T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], T5], + t1, + t2, + t3, + t4, + t5, + ) +} + +// SequenceTuple5 converts a [Tuple5] of [Option[T]] into an [Option[Tuple5]]. +func SequenceTuple5[T1, T2, T3, T4, T5 any](t T.Tuple5[Option[T1], Option[T2], Option[T3], Option[T4], Option[T5]]) Option[T.Tuple5[T1, T2, T3, T4, T5]] { + return A.SequenceTuple5( + Map[T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], T5], + t, + ) +} + +// TraverseTuple5 converts a [Tuple5] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple5]]. +func TraverseTuple5[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], F3 ~func(A3) Option[T3], F4 ~func(A4) Option[T4], F5 ~func(A5) Option[T5], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(T.Tuple5[A1, A2, A3, A4, A5]) Option[T.Tuple5[T1, T2, T3, T4, T5]] { + return func(t T.Tuple5[A1, A2, A3, A4, A5]) Option[T.Tuple5[T1, T2, T3, T4, T5]] { + return A.TraverseTuple5( + Map[T1, func(T2) func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5]], + Ap[func(T3) func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T2], + Ap[func(T4) func(T5) T.Tuple5[T1, T2, T3, T4, T5], T3], + Ap[func(T5) T.Tuple5[T1, T2, T3, T4, T5], T4], + Ap[T.Tuple5[T1, T2, T3, T4, T5], T5], + f1, + f2, + f3, + f4, + f5, + t, + ) + } +} + +// Optionize6 converts a function with 6 parameters returning a tuple of a return value R and a boolean into a function with 6 parameters returning an Option[R] +func Optionize6[F ~func(T0, T1, T2, T3, T4, T5) (R, bool), T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) Option[R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1, t2, t3, t4, t5) + }) + } +} + +// Unoptionize6 converts a function with 6 parameters returning a tuple of a return value R and a boolean into a function with 6 parameters returning an Option[R] +func Unoptionize6[F ~func(T0, T1, T2, T3, T4, T5) Option[R], T0, T1, T2, T3, T4, T5, R any](f F) func(T0, T1, T2, T3, T4, T5) (R, bool) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) (R, bool) { + return Unwrap(f(t0, t1, t2, t3, t4, t5)) + } +} + +// SequenceT6 converts 6 parameters of [Option[T]] into a [Option[Tuple6]]. +func SequenceT6[T1, T2, T3, T4, T5, T6 any](t1 Option[T1], t2 Option[T2], t3 Option[T3], t4 Option[T4], t5 Option[T5], t6 Option[T6]) Option[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return A.SequenceT6( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], T6], + t1, + t2, + t3, + t4, + t5, + t6, + ) +} + +// SequenceTuple6 converts a [Tuple6] of [Option[T]] into an [Option[Tuple6]]. +func SequenceTuple6[T1, T2, T3, T4, T5, T6 any](t T.Tuple6[Option[T1], Option[T2], Option[T3], Option[T4], Option[T5], Option[T6]]) Option[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return A.SequenceTuple6( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], T6], + t, + ) +} + +// TraverseTuple6 converts a [Tuple6] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple6]]. +func TraverseTuple6[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], F3 ~func(A3) Option[T3], F4 ~func(A4) Option[T4], F5 ~func(A5) Option[T5], F6 ~func(A6) Option[T6], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(T.Tuple6[A1, A2, A3, A4, A5, A6]) Option[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return func(t T.Tuple6[A1, A2, A3, A4, A5, A6]) Option[T.Tuple6[T1, T2, T3, T4, T5, T6]] { + return A.TraverseTuple6( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6]], + Ap[func(T3) func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T2], + Ap[func(T4) func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T3], + Ap[func(T5) func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T4], + Ap[func(T6) T.Tuple6[T1, T2, T3, T4, T5, T6], T5], + Ap[T.Tuple6[T1, T2, T3, T4, T5, T6], T6], + f1, + f2, + f3, + f4, + f5, + f6, + t, + ) + } +} + +// Optionize7 converts a function with 7 parameters returning a tuple of a return value R and a boolean into a function with 7 parameters returning an Option[R] +func Optionize7[F ~func(T0, T1, T2, T3, T4, T5, T6) (R, bool), T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) Option[R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1, t2, t3, t4, t5, t6) + }) + } +} + +// Unoptionize7 converts a function with 7 parameters returning a tuple of a return value R and a boolean into a function with 7 parameters returning an Option[R] +func Unoptionize7[F ~func(T0, T1, T2, T3, T4, T5, T6) Option[R], T0, T1, T2, T3, T4, T5, T6, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) (R, bool) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) (R, bool) { + return Unwrap(f(t0, t1, t2, t3, t4, t5, t6)) + } +} + +// SequenceT7 converts 7 parameters of [Option[T]] into a [Option[Tuple7]]. +func SequenceT7[T1, T2, T3, T4, T5, T6, T7 any](t1 Option[T1], t2 Option[T2], t3 Option[T3], t4 Option[T4], t5 Option[T5], t6 Option[T6], t7 Option[T7]) Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return A.SequenceT7( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T7], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + ) +} + +// SequenceTuple7 converts a [Tuple7] of [Option[T]] into an [Option[Tuple7]]. +func SequenceTuple7[T1, T2, T3, T4, T5, T6, T7 any](t T.Tuple7[Option[T1], Option[T2], Option[T3], Option[T4], Option[T5], Option[T6], Option[T7]]) Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return A.SequenceTuple7( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T7], + t, + ) +} + +// TraverseTuple7 converts a [Tuple7] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple7]]. +func TraverseTuple7[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], F3 ~func(A3) Option[T3], F4 ~func(A4) Option[T4], F5 ~func(A5) Option[T5], F6 ~func(A6) Option[T6], F7 ~func(A7) Option[T7], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return func(t T.Tuple7[A1, A2, A3, A4, A5, A6, A7]) Option[T.Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return A.TraverseTuple7( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T2], + Ap[func(T4) func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T3], + Ap[func(T5) func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T4], + Ap[func(T6) func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T5], + Ap[func(T7) T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T6], + Ap[T.Tuple7[T1, T2, T3, T4, T5, T6, T7], T7], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + t, + ) + } +} + +// Optionize8 converts a function with 8 parameters returning a tuple of a return value R and a boolean into a function with 8 parameters returning an Option[R] +func Optionize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) (R, bool), T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) Option[R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1, t2, t3, t4, t5, t6, t7) + }) + } +} + +// Unoptionize8 converts a function with 8 parameters returning a tuple of a return value R and a boolean into a function with 8 parameters returning an Option[R] +func Unoptionize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) Option[R], T0, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) (R, bool) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) (R, bool) { + return Unwrap(f(t0, t1, t2, t3, t4, t5, t6, t7)) + } +} + +// SequenceT8 converts 8 parameters of [Option[T]] into a [Option[Tuple8]]. +func SequenceT8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 Option[T1], t2 Option[T2], t3 Option[T3], t4 Option[T4], t5 Option[T5], t6 Option[T6], t7 Option[T7], t8 Option[T8]) Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return A.SequenceT8( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T8], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + ) +} + +// SequenceTuple8 converts a [Tuple8] of [Option[T]] into an [Option[Tuple8]]. +func SequenceTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t T.Tuple8[Option[T1], Option[T2], Option[T3], Option[T4], Option[T5], Option[T6], Option[T7], Option[T8]]) Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return A.SequenceTuple8( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T8], + t, + ) +} + +// TraverseTuple8 converts a [Tuple8] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple8]]. +func TraverseTuple8[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], F3 ~func(A3) Option[T3], F4 ~func(A4) Option[T4], F5 ~func(A5) Option[T5], F6 ~func(A6) Option[T6], F7 ~func(A7) Option[T7], F8 ~func(A8) Option[T8], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return func(t T.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) Option[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return A.TraverseTuple8( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T3], + Ap[func(T5) func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T4], + Ap[func(T6) func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T5], + Ap[func(T7) func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T6], + Ap[func(T8) T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T7], + Ap[T.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8], T8], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + t, + ) + } +} + +// Optionize9 converts a function with 9 parameters returning a tuple of a return value R and a boolean into a function with 9 parameters returning an Option[R] +func Optionize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, bool), T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) Option[R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8) + }) + } +} + +// Unoptionize9 converts a function with 9 parameters returning a tuple of a return value R and a boolean into a function with 9 parameters returning an Option[R] +func Unoptionize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) Option[R], T0, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, bool) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) (R, bool) { + return Unwrap(f(t0, t1, t2, t3, t4, t5, t6, t7, t8)) + } +} + +// SequenceT9 converts 9 parameters of [Option[T]] into a [Option[Tuple9]]. +func SequenceT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 Option[T1], t2 Option[T2], t3 Option[T3], t4 Option[T4], t5 Option[T5], t6 Option[T6], t7 Option[T7], t8 Option[T8], t9 Option[T9]) Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return A.SequenceT9( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T9], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + ) +} + +// SequenceTuple9 converts a [Tuple9] of [Option[T]] into an [Option[Tuple9]]. +func SequenceTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t T.Tuple9[Option[T1], Option[T2], Option[T3], Option[T4], Option[T5], Option[T6], Option[T7], Option[T8], Option[T9]]) Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return A.SequenceTuple9( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T9], + t, + ) +} + +// TraverseTuple9 converts a [Tuple9] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple9]]. +func TraverseTuple9[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], F3 ~func(A3) Option[T3], F4 ~func(A4) Option[T4], F5 ~func(A5) Option[T5], F6 ~func(A6) Option[T6], F7 ~func(A7) Option[T7], F8 ~func(A8) Option[T8], F9 ~func(A9) Option[T9], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return func(t T.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) Option[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return A.TraverseTuple9( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T4], + Ap[func(T6) func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T5], + Ap[func(T7) func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T6], + Ap[func(T8) func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T7], + Ap[func(T9) T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T8], + Ap[T.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9], T9], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + t, + ) + } +} + +// Optionize10 converts a function with 10 parameters returning a tuple of a return value R and a boolean into a function with 10 parameters returning an Option[R] +func Optionize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, bool), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) Option[R] { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) Option[R] { + return optionize(func() (R, bool) { + return f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) + }) + } +} + +// Unoptionize10 converts a function with 10 parameters returning a tuple of a return value R and a boolean into a function with 10 parameters returning an Option[R] +func Unoptionize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) Option[R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, bool) { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) (R, bool) { + return Unwrap(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)) + } +} + +// SequenceT10 converts 10 parameters of [Option[T]] into a [Option[Tuple10]]. +func SequenceT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 Option[T1], t2 Option[T2], t3 Option[T3], t4 Option[T4], t5 Option[T5], t6 Option[T6], t7 Option[T7], t8 Option[T8], t9 Option[T9], t10 Option[T10]) Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return A.SequenceT10( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T10], + t1, + t2, + t3, + t4, + t5, + t6, + t7, + t8, + t9, + t10, + ) +} + +// SequenceTuple10 converts a [Tuple10] of [Option[T]] into an [Option[Tuple10]]. +func SequenceTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t T.Tuple10[Option[T1], Option[T2], Option[T3], Option[T4], Option[T5], Option[T6], Option[T7], Option[T8], Option[T9], Option[T10]]) Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return A.SequenceTuple10( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T10], + t, + ) +} + +// TraverseTuple10 converts a [Tuple10] of [A] via transformation functions transforming [A] to [Option[A]] into a [Option[Tuple10]]. +func TraverseTuple10[F1 ~func(A1) Option[T1], F2 ~func(A2) Option[T2], F3 ~func(A3) Option[T3], F4 ~func(A4) Option[T4], F5 ~func(A5) Option[T5], F6 ~func(A6) Option[T6], F7 ~func(A7) Option[T7], F8 ~func(A8) Option[T8], F9 ~func(A9) Option[T9], F10 ~func(A10) Option[T10], A1, T1, A2, T2, A3, T3, A4, T4, A5, T5, A6, T6, A7, T7, A8, T8, A9, T9, A10, T10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return func(t T.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) Option[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return A.TraverseTuple10( + Map[T1, func(T2) func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]], + Ap[func(T3) func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T2], + Ap[func(T4) func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T3], + Ap[func(T5) func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T4], + Ap[func(T6) func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T5], + Ap[func(T7) func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T6], + Ap[func(T8) func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T7], + Ap[func(T9) func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T8], + Ap[func(T10) T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T9], + Ap[T.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T10], + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + t, + ) + } +} diff --git a/v2/option/logger.go b/v2/option/logger.go new file mode 100644 index 0000000..5646572 --- /dev/null +++ b/v2/option/logger.go @@ -0,0 +1,48 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "log" + + F "github.com/IBM/fp-go/v2/function" + L "github.com/IBM/fp-go/v2/logging" +) + +func _log[A any](left func(string, ...any), right func(string, ...any), prefix string) func(Option[A]) Option[A] { + return Fold( + func() Option[A] { + left("%s", prefix) + return None[A]() + }, + func(a A) Option[A] { + right("%s: %v", prefix, a) + return Some(a) + }) +} + +func Logger[A any](loggers ...*log.Logger) func(string) func(Option[A]) Option[A] { + left, right := L.LoggingCallbacks(loggers...) + return func(prefix string) func(Option[A]) Option[A] { + delegate := _log[A](left, right, prefix) + return func(ma Option[A]) Option[A] { + return F.Pipe1( + delegate(ma), + ChainTo[A](ma), + ) + } + } +} diff --git a/v2/option/monad.go b/v2/option/monad.go new file mode 100644 index 0000000..8925ed2 --- /dev/null +++ b/v2/option/monad.go @@ -0,0 +1,43 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "github.com/IBM/fp-go/v2/internal/monad" +) + +type optionMonad[A, B any] struct{} + +func (o *optionMonad[A, B]) Of(a A) Option[A] { + return Of[A](a) +} + +func (o *optionMonad[A, B]) Map(f func(A) B) func(Option[A]) Option[B] { + return Map[A, B](f) +} + +func (o *optionMonad[A, B]) Chain(f func(A) Option[B]) func(Option[A]) Option[B] { + return Chain[A, B](f) +} + +func (o *optionMonad[A, B]) Ap(fa Option[A]) func(Option[func(A) B]) Option[B] { + return Ap[B, A](fa) +} + +// Monad implements the monadic operations for [Option] +func Monad[A, B any]() monad.Monad[A, B, Option[A], Option[B], Option[func(A) B]] { + return &optionMonad[A, B]{} +} diff --git a/v2/option/monoid.go b/v2/option/monoid.go new file mode 100644 index 0000000..bbaafd4 --- /dev/null +++ b/v2/option/monoid.go @@ -0,0 +1,72 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func Semigroup[A any]() func(S.Semigroup[A]) S.Semigroup[Option[A]] { + return func(s S.Semigroup[A]) S.Semigroup[Option[A]] { + concat := s.Concat + return S.MakeSemigroup( + func(x, y Option[A]) Option[A] { + return MonadFold(x, F.Constant(y), func(left A) Option[A] { + return MonadFold(y, F.Constant(x), func(right A) Option[A] { + return Some(concat(left, right)) + }) + }) + }, + ) + } +} + +// Monoid returning the left-most non-`None` value. If both operands are `Some`s then the inner values are +// concatenated using the provided `Semigroup` +// +// | x | y | concat(x, y) | +// | ------- | ------- | ------------------ | +// | none | none | none | +// | some(a) | none | some(a) | +// | none | some(b) | some(b) | +// | some(a) | some(b) | some(concat(a, b)) | +func Monoid[A any]() func(S.Semigroup[A]) M.Monoid[Option[A]] { + sg := Semigroup[A]() + return func(s S.Semigroup[A]) M.Monoid[Option[A]] { + return M.MakeMonoid(sg(s).Concat, None[A]()) + } +} + +// AlternativeMonoid is the alternative [Monoid] for an [Option] +func AlternativeMonoid[A any](m M.Monoid[A]) M.Monoid[Option[A]] { + return M.AlternativeMonoid( + Of[A], + MonadMap[A, func(A) A], + MonadAp[A, A], + MonadAlt[A], + m, + ) +} + +// AltMonoid is the alternative [Monoid] for an [Option] +func AltMonoid[A any]() M.Monoid[Option[A]] { + return M.AltMonoid( + None[A], + MonadAlt[A], + ) +} diff --git a/v2/option/number/number.go b/v2/option/number/number.go new file mode 100644 index 0000000..a83cda1 --- /dev/null +++ b/v2/option/number/number.go @@ -0,0 +1,35 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package number + +import ( + "strconv" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" +) + +func atoi(value string) (int, bool) { + data, err := strconv.Atoi(value) + return data, err == nil +} + +var ( + // Atoi converts a string to an integer + Atoi = O.Optionize1(atoi) + // Itoa converts an integer to a string + Itoa = F.Flow2(strconv.Itoa, O.Of[string]) +) diff --git a/v2/option/option.go b/v2/option/option.go new file mode 100644 index 0000000..e4fb617 --- /dev/null +++ b/v2/option/option.go @@ -0,0 +1,168 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// package option implements the Option monad, a data type that can have a defined value or none +package option + +import ( + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + FC "github.com/IBM/fp-go/v2/internal/functor" +) + +func fromPredicate[A any](a A, pred func(A) bool) Option[A] { + if pred(a) { + return Some(a) + } + return None[A]() +} + +func FromPredicate[A any](pred func(A) bool) func(A) Option[A] { + return F.Bind2nd(fromPredicate[A], pred) +} + +func FromNillable[A any](a *A) Option[*A] { + return fromPredicate(a, F.IsNonNil[A]) +} + +func FromValidation[A, B any](f func(A) (B, bool)) func(A) Option[B] { + return Optionize1(f) +} + +// MonadAp is the applicative functor of Option +func MonadAp[B, A any](fab Option[func(A) B], fa Option[A]) Option[B] { + return MonadFold(fab, None[B], func(ab func(A) B) Option[B] { + return MonadFold(fa, None[B], F.Flow2(ab, Some[B])) + }) +} + +// Ap is the applicative functor of Option +func Ap[B, A any](fa Option[A]) func(Option[func(A) B]) Option[B] { + return F.Bind2nd(MonadAp[B, A], fa) +} + +func MonadMap[A, B any](fa Option[A], f func(A) B) Option[B] { + return MonadChain(fa, F.Flow2(f, Some[B])) +} + +func Map[A, B any](f func(a A) B) func(Option[A]) Option[B] { + return Chain(F.Flow2(f, Some[B])) +} + +func MonadMapTo[A, B any](fa Option[A], b B) Option[B] { + return MonadMap(fa, F.Constant1[A](b)) +} + +func MapTo[A, B any](b B) func(Option[A]) Option[B] { + return F.Bind2nd(MonadMapTo[A, B], b) +} + +func TryCatch[A any](f func() (A, error)) Option[A] { + val, err := f() + if err != nil { + return None[A]() + } + return Some(val) +} + +func Fold[A, B any](onNone func() B, onSome func(a A) B) func(ma Option[A]) B { + return func(ma Option[A]) B { + return MonadFold(ma, onNone, onSome) + } +} + +func MonadGetOrElse[A any](fa Option[A], onNone func() A) A { + return MonadFold(fa, onNone, F.Identity[A]) +} + +func GetOrElse[A any](onNone func() A) func(Option[A]) A { + return Fold(onNone, F.Identity[A]) +} + +func MonadChain[A, B any](fa Option[A], f func(A) Option[B]) Option[B] { + return MonadFold(fa, None[B], f) +} + +func Chain[A, B any](f func(A) Option[B]) func(Option[A]) Option[B] { + return Fold(None[B], f) +} + +func MonadChainTo[A, B any](_ Option[A], mb Option[B]) Option[B] { + return mb +} + +func ChainTo[A, B any](mb Option[B]) func(Option[A]) Option[B] { + return F.Bind2nd(MonadChainTo[A, B], mb) +} + +func MonadChainFirst[A, B any](ma Option[A], f func(A) Option[B]) Option[A] { + return C.MonadChainFirst( + MonadChain[A, A], + MonadMap[B, A], + ma, + f, + ) +} + +func ChainFirst[A, B any](f func(A) Option[B]) func(Option[A]) Option[A] { + return C.ChainFirst( + Chain[A, A], + Map[B, A], + f, + ) +} + +func Flatten[A any](mma Option[Option[A]]) Option[A] { + return MonadChain(mma, F.Identity[Option[A]]) +} + +func MonadAlt[A any](fa Option[A], that func() Option[A]) Option[A] { + return MonadFold(fa, that, Of[A]) +} + +func Alt[A any](that func() Option[A]) func(Option[A]) Option[A] { + return Fold(that, Of[A]) +} + +func MonadSequence2[T1, T2, R any](o1 Option[T1], o2 Option[T2], f func(T1, T2) Option[R]) Option[R] { + return MonadFold(o1, None[R], func(t1 T1) Option[R] { + return MonadFold(o2, None[R], func(t2 T2) Option[R] { + return f(t1, t2) + }) + }) +} + +func Sequence2[T1, T2, R any](f func(T1, T2) Option[R]) func(Option[T1], Option[T2]) Option[R] { + return func(o1 Option[T1], o2 Option[T2]) Option[R] { + return MonadSequence2(o1, o2, f) + } +} + +func Reduce[A, B any](f func(B, A) B, initial B) func(Option[A]) B { + return Fold(F.Constant(initial), F.Bind1st(f, initial)) +} + +// Filter converts an optional onto itself if it is some and the predicate is true +func Filter[A any](pred func(A) bool) func(Option[A]) Option[A] { + return Fold(None[A], F.Ternary(pred, Of[A], F.Ignore1of1[A](None[A]))) +} + +func MonadFlap[B, A any](fab Option[func(A) B], a A) Option[B] { + return FC.MonadFlap(MonadMap[func(A) B, B], fab, a) +} + +func Flap[B, A any](a A) func(Option[func(A) B]) Option[B] { + return FC.Flap(Map[func(A) B, B], a) +} diff --git a/v2/option/option_test.go b/v2/option/option_test.go new file mode 100644 index 0000000..e172058 --- /dev/null +++ b/v2/option/option_test.go @@ -0,0 +1,176 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "encoding/json" + "fmt" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type ( + SampleData struct { + Value string + OptValue Option[string] + } +) + +func TestJson(t *testing.T) { + + sample := SampleData{ + Value: "value", + OptValue: Of("optValue"), + } + + data, err := json.Marshal(&sample) + require.NoError(t, err) + + var deser SampleData + err = json.Unmarshal(data, &deser) + require.NoError(t, err) + + assert.Equal(t, sample, deser) + + sample = SampleData{ + Value: "value", + OptValue: None[string](), + } + + data, err = json.Marshal(&sample) + require.NoError(t, err) + + err = json.Unmarshal(data, &deser) + require.NoError(t, err) + + assert.Equal(t, sample, deser) +} + +func TestDefault(t *testing.T) { + var e Option[string] + + assert.Equal(t, None[string](), e) +} + +func TestReduce(t *testing.T) { + + assert.Equal(t, 2, F.Pipe1(None[int](), Reduce(utils.Sum, 2))) + assert.Equal(t, 5, F.Pipe1(Some(3), Reduce(utils.Sum, 2))) +} + +func TestIsNone(t *testing.T) { + assert.True(t, IsNone(None[int]())) + assert.False(t, IsNone(Of(1))) +} + +func TestIsSome(t *testing.T) { + assert.True(t, IsSome(Of(1))) + assert.False(t, IsSome(None[int]())) +} + +func TestMapOption(t *testing.T) { + + assert.Equal(t, F.Pipe1(Some(2), Map(utils.Double)), Some(4)) + + assert.Equal(t, F.Pipe1(None[int](), Map(utils.Double)), None[int]()) +} + +func TestTryCachOption(t *testing.T) { + + res := TryCatch(utils.Error) + + assert.Equal(t, None[int](), res) +} + +func TestAp(t *testing.T) { + assert.Equal(t, Some(4), F.Pipe1( + Some(utils.Double), + Ap[int, int](Some(2)), + )) + + assert.Equal(t, None[int](), F.Pipe1( + Some(utils.Double), + Ap[int, int](None[int]()), + )) + + assert.Equal(t, None[int](), F.Pipe1( + None[func(int) int](), + Ap[int, int](Some(2)), + )) + + assert.Equal(t, None[int](), F.Pipe1( + None[func(int) int](), + Ap[int, int](None[int]()), + )) +} + +func TestChain(t *testing.T) { + f := func(n int) Option[int] { return Some(n * 2) } + g := func(_ int) Option[int] { return None[int]() } + + assert.Equal(t, Some(2), F.Pipe1( + Some(1), + Chain(f), + )) + + assert.Equal(t, None[int](), F.Pipe1( + None[int](), + Chain(f), + )) + + assert.Equal(t, None[int](), F.Pipe1( + Some(1), + Chain(g), + )) + + assert.Equal(t, None[int](), F.Pipe1( + None[int](), + Chain(g), + )) +} + +func TestFlatten(t *testing.T) { + assert.Equal(t, Of(1), F.Pipe1(Of(Of(1)), Flatten[int])) +} + +func TestFold(t *testing.T) { + f := F.Constant("none") + g := func(s string) string { return fmt.Sprintf("some%d", len(s)) } + + fold := Fold(f, g) + + assert.Equal(t, "none", fold(None[string]())) + assert.Equal(t, "some3", fold(Some("abc"))) +} + +func TestFromPredicate(t *testing.T) { + p := func(n int) bool { return n > 2 } + f := FromPredicate(p) + + assert.Equal(t, None[int](), f(1)) + assert.Equal(t, Some(3), f(3)) +} + +func TestAlt(t *testing.T) { + assert.Equal(t, Some(1), F.Pipe1(Some(1), Alt(F.Constant(Some(2))))) + assert.Equal(t, Some(2), F.Pipe1(Some(2), Alt(F.Constant(None[int]())))) + assert.Equal(t, Some(1), F.Pipe1(None[int](), Alt(F.Constant(Some(1))))) + assert.Equal(t, None[int](), F.Pipe1(None[int](), Alt(F.Constant(None[int]())))) +} diff --git a/v2/option/ord.go b/v2/option/ord.go new file mode 100644 index 0000000..23f70d6 --- /dev/null +++ b/v2/option/ord.go @@ -0,0 +1,38 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + C "github.com/IBM/fp-go/v2/constraints" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/ord" +) + +// Constructs an order for [Option] +func Ord[A any](a ord.Ord[A]) ord.Ord[Option[A]] { + // some convenient shortcuts + fld := Fold( + F.Constant(Fold(F.Constant(0), F.Constant1[A](-1))), + F.Flow2(F.Curry2(a.Compare), F.Bind1st(Fold[A, int], F.Constant(1))), + ) + // convert to an ordering predicate + return ord.MakeOrd(F.Uncurry2(fld), Eq(ord.ToEq(a)).Equals) +} + +// FromStrictCompare constructs an [Ord] from the canonical comparison function +func FromStrictCompare[A C.Ordered]() ord.Ord[Option[A]] { + return Ord(ord.FromStrictCompare[A]()) +} diff --git a/v2/option/ord_test.go b/v2/option/ord_test.go new file mode 100644 index 0000000..e69d223 --- /dev/null +++ b/v2/option/ord_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "testing" + + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +// it('getOrd', () => { +// const OS = _.getOrd(S.Ord) +// U.deepStrictEqual(OS.compare(_.none, _.none), 0) +// U.deepStrictEqual(OS.compare(_.some('a'), _.none), 1) +// U.deepStrictEqual(OS.compare(_.none, _.some('a')), -1) +// U.deepStrictEqual(OS.compare(_.some('a'), _.some('a')), 0) +// U.deepStrictEqual(OS.compare(_.some('a'), _.some('b')), -1) +// U.deepStrictEqual(OS.compare(_.some('b'), _.some('a')), 1) +// }) + +func TestOrd(t *testing.T) { + + os := Ord(S.Ord) + + assert.Equal(t, 0, os.Compare(None[string](), None[string]())) + assert.Equal(t, 1, os.Compare(Some("a"), None[string]())) + assert.Equal(t, -1, os.Compare(None[string](), Some("a"))) + assert.Equal(t, 0, os.Compare(Some("a"), Some("a"))) + assert.Equal(t, -1, os.Compare(Some("a"), Some("b"))) + assert.Equal(t, 1, os.Compare(Some("b"), Some("a"))) + +} diff --git a/v2/option/pair.go b/v2/option/pair.go new file mode 100644 index 0000000..3a25c94 --- /dev/null +++ b/v2/option/pair.go @@ -0,0 +1,30 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + P "github.com/IBM/fp-go/v2/pair" + PG "github.com/IBM/fp-go/v2/pair/generic" +) + +// SequencePair converts a [Pair] of [Option[T]] into an [Option[Pair]]. +func SequencePair[T1, T2 any](t P.Pair[Option[T1], Option[T2]]) Option[P.Pair[T1, T2]] { + return PG.SequencePair( + Map[T1, func(T2) P.Pair[T1, T2]], + Ap[P.Pair[T1, T2], T2], + t, + ) +} diff --git a/v2/option/pointed.go b/v2/option/pointed.go new file mode 100644 index 0000000..0e5d9d0 --- /dev/null +++ b/v2/option/pointed.go @@ -0,0 +1,31 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "github.com/IBM/fp-go/v2/internal/pointed" +) + +type optionPointed[A any] struct{} + +func (o *optionPointed[A]) Of(a A) Option[A] { + return Of[A](a) +} + +// Pointed implements the Pointed operations for [Option] +func Pointed[A any]() pointed.Pointed[A, Option[A]] { + return &optionPointed[A]{} +} diff --git a/v2/option/record.go b/v2/option/record.go new file mode 100644 index 0000000..88863c0 --- /dev/null +++ b/v2/option/record.go @@ -0,0 +1,80 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + F "github.com/IBM/fp-go/v2/function" + RR "github.com/IBM/fp-go/v2/internal/record" +) + +// TraverseRecordG transforms a record of options into an option of a record +func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](f func(A) Option[B]) func(GA) Option[GB] { + return RR.Traverse[GA]( + Of[GB], + Map[GB, func(B) GB], + Ap[GB, B], + + f, + ) +} + +// TraverseRecord transforms a record of options into an option of a record +func TraverseRecord[K comparable, A, B any](f func(A) Option[B]) func(map[K]A) Option[map[K]B] { + return TraverseRecordG[map[K]A, map[K]B](f) +} + +// TraverseRecordWithIndexG transforms a record of options into an option of a record +func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](f func(K, A) Option[B]) func(GA) Option[GB] { + return RR.TraverseWithIndex[GA]( + Of[GB], + Map[GB, func(B) GB], + Ap[GB, B], + + f, + ) +} + +// TraverseRecordWithIndex transforms a record of options into an option of a record +func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) Option[B]) func(map[K]A) Option[map[K]B] { + return TraverseRecordWithIndexG[map[K]A, map[K]B](f) +} + +func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Option[A], K comparable, A any](ma GOA) Option[GA] { + return TraverseRecordG[GOA, GA](F.Identity[Option[A]])(ma) +} + +// SequenceRecord converts a homogeneous sequence of either into an either of sequence +func SequenceRecord[K comparable, A any](ma map[K]Option[A]) Option[map[K]A] { + return SequenceRecordG[map[K]A](ma) +} + +func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M { + r[k] = v + return r +} + +// CompactRecordG discards the noe values and keeps the some values +func CompactRecordG[M1 ~map[K]Option[A], M2 ~map[K]A, K comparable, A any](m M1) M2 { + bnd := F.Bind12of3(upsertAtReadWrite[M2]) + return RR.ReduceWithIndex(m, func(key K, m M2, value Option[A]) M2 { + return MonadFold(value, F.Constant(m), bnd(m, key)) + }, make(M2)) +} + +// CompactRecord discards the noe values and keeps the some values +func CompactRecord[K comparable, A any](m map[K]Option[A]) map[K]A { + return CompactRecordG[map[K]Option[A], map[K]A](m) +} diff --git a/v2/option/record_test.go b/v2/option/record_test.go new file mode 100644 index 0000000..1800990 --- /dev/null +++ b/v2/option/record_test.go @@ -0,0 +1,54 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "fmt" + "testing" + + TST "github.com/IBM/fp-go/v2/internal/testing" + "github.com/stretchr/testify/assert" +) + +func TestCompactRecord(t *testing.T) { + // make the map + m := make(map[string]Option[int]) + m["foo"] = None[int]() + m["bar"] = Some(1) + // compact it + m1 := CompactRecord(m) + // check expected + exp := map[string]int{ + "bar": 1, + } + + assert.Equal(t, exp, m1) +} + +func TestSequenceRecord(t *testing.T) { + + s := TST.SequenceRecordTest( + FromStrictEquals[bool](), + Pointed[string](), + Pointed[bool](), + Functor[map[string]string, bool](), + SequenceRecord[string, string], + ) + + for i := 0; i < 10; i++ { + t.Run(fmt.Sprintf("TestSequenceRecord %d", i), s(i)) + } +} diff --git a/v2/option/sequence.go b/v2/option/sequence.go new file mode 100644 index 0000000..f1ac18b --- /dev/null +++ b/v2/option/sequence.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// Sequence converts an [Option] of some higher kinded type into the higher kinded type of an [Option] +func Sequence[A, HKTA, HKTOA any]( + mof func(Option[A]) HKTOA, + mmap func(func(A) Option[A]) func(HKTA) HKTOA, +) func(Option[HKTA]) HKTOA { + return Fold(F.Nullary2(None[A], mof), mmap(Some[A])) +} + +// Traverse converts an [Option] of some higher kinded type into the higher kinded type of an [Option] +func Traverse[A, B, HKTB, HKTOB any]( + mof func(Option[B]) HKTOB, + mmap func(func(B) Option[B]) func(HKTB) HKTOB, +) func(func(A) HKTB) func(Option[A]) HKTOB { + onNone := F.Nullary2(None[B], mof) + onSome := mmap(Some[B]) + return func(f func(A) HKTB) func(Option[A]) HKTOB { + return Fold(onNone, F.Flow2(f, onSome)) + } +} diff --git a/v2/option/sequence_test.go b/v2/option/sequence_test.go new file mode 100644 index 0000000..b2b0c6d --- /dev/null +++ b/v2/option/sequence_test.go @@ -0,0 +1,44 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "testing" + + T "github.com/IBM/fp-go/v2/tuple" + "github.com/stretchr/testify/assert" +) + +func TestSequenceT(t *testing.T) { + // one argumemt + s1 := SequenceT1[int] + assert.Equal(t, Of(T.MakeTuple1(1)), s1(Of(1))) + + // two arguments + s2 := SequenceT2[int, string] + assert.Equal(t, Of(T.MakeTuple2(1, "a")), s2(Of(1), Of("a"))) + + // three arguments + s3 := SequenceT3[int, string, bool] + assert.Equal(t, Of(T.MakeTuple3(1, "a", true)), s3(Of(1), Of("a"), Of(true))) + + // four arguments + s4 := SequenceT4[int, string, bool, int] + assert.Equal(t, Of(T.MakeTuple4(1, "a", true, 2)), s4(Of(1), Of("a"), Of(true), Of(2))) + + // three with one none + assert.Equal(t, None[T.Tuple3[int, string, bool]](), s3(Of(1), Of("a"), None[bool]())) +} diff --git a/v2/option/testing/laws.go b/v2/option/testing/laws.go new file mode 100644 index 0000000..3e0a567 --- /dev/null +++ b/v2/option/testing/laws.go @@ -0,0 +1,74 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" + O "github.com/IBM/fp-go/v2/option" +) + +// AssertLaws asserts the apply monad laws for the [Option] monad +func AssertLaws[A, B, C any](t *testing.T, + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + return L.AssertLaws(t, + O.Eq(eqa), + O.Eq(eqb), + O.Eq(eqc), + + O.Of[A], + O.Of[B], + O.Of[C], + + O.Of[func(A) A], + O.Of[func(A) B], + O.Of[func(B) C], + O.Of[func(func(A) B) B], + + O.MonadMap[A, A], + O.MonadMap[A, B], + O.MonadMap[A, C], + O.MonadMap[B, C], + + O.MonadMap[func(B) C, func(func(A) B) func(A) C], + + O.MonadChain[A, A], + O.MonadChain[A, B], + O.MonadChain[A, C], + O.MonadChain[B, C], + + O.MonadAp[A, A], + O.MonadAp[B, A], + O.MonadAp[C, B], + O.MonadAp[C, A], + + O.MonadAp[B, func(A) B], + O.MonadAp[func(A) C, func(A) B], + + ab, + bc, + ) + +} diff --git a/v2/option/testing/laws_test.go b/v2/option/testing/laws_test.go new file mode 100644 index 0000000..63b23e1 --- /dev/null +++ b/v2/option/testing/laws_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqa, eqb, eqc, ab, bc) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/option/type.go b/v2/option/type.go new file mode 100644 index 0000000..ebff976 --- /dev/null +++ b/v2/option/type.go @@ -0,0 +1,36 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +func toType[T any](a any) (T, bool) { + b, ok := a.(T) + return b, ok +} + +func ToType[T any](src any) Option[T] { + return F.Pipe1( + src, + Optionize1(toType[T]), + ) +} + +func ToAny[T any](src T) Option[any] { + return Of(any(src)) +} diff --git a/v2/option/type_test.go b/v2/option/type_test.go new file mode 100644 index 0000000..d400dae --- /dev/null +++ b/v2/option/type_test.go @@ -0,0 +1,37 @@ +// Copyright (c) 2025 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTypeConversion(t *testing.T) { + + var src any = "Carsten" + + dst := ToType[string](src) + assert.Equal(t, Some("Carsten"), dst) +} + +func TestInvalidConversion(t *testing.T) { + var src any = make(map[string]string) + + dst := ToType[int](src) + assert.Equal(t, None[int](), dst) +} diff --git a/v2/ord/monoid.go b/v2/ord/monoid.go new file mode 100644 index 0000000..8c0c448 --- /dev/null +++ b/v2/ord/monoid.go @@ -0,0 +1,52 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ord + +import ( + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// Semigroup implements a two level ordering +func Semigroup[A any]() S.Semigroup[Ord[A]] { + return S.MakeSemigroup(func(first, second Ord[A]) Ord[A] { + return FromCompare(func(a, b A) int { + ox := first.Compare(a, b) + if ox != 0 { + return ox + } + return second.Compare(a, b) + }) + }) +} + +// Monoid implements a two level ordering such that +// - its `Concat(ord1, ord2)` operation will order first by `ord1`, and then by `ord2` +// - its `Empty` value is an `Ord` that always considers compared elements equal +func Monoid[A any]() M.Monoid[Ord[A]] { + return M.MakeMonoid(Semigroup[A]().Concat, FromCompare(F.Constant2[A, A](0))) +} + +// MaxSemigroup returns a semigroup where `concat` will return the maximum, based on the provided order. +func MaxSemigroup[A any](o Ord[A]) S.Semigroup[A] { + return S.MakeSemigroup(Max(o)) +} + +// MaxSemigroup returns a semigroup where `concat` will return the minimum, based on the provided order. +func MinSemigroup[A any](o Ord[A]) S.Semigroup[A] { + return S.MakeSemigroup(Min(o)) +} diff --git a/v2/ord/ord.go b/v2/ord/ord.go new file mode 100644 index 0000000..b129cd4 --- /dev/null +++ b/v2/ord/ord.go @@ -0,0 +1,180 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ord + +import ( + C "github.com/IBM/fp-go/v2/constraints" + E "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" + P "github.com/IBM/fp-go/v2/predicate" +) + +type Ord[T any] interface { + E.Eq[T] + Compare(x, y T) int +} + +type ord[T any] struct { + c func(x, y T) int + e func(x, y T) bool +} + +func (self ord[T]) Equals(x, y T) bool { + return self.e(x, y) +} + +func (self ord[T]) Compare(x, y T) int { + return self.c(x, y) +} + +// ToEq converts an [Ord] to [E.Eq] +func ToEq[T any](o Ord[T]) E.Eq[T] { + return o +} + +// MakeOrd creates an instance of an Ord +func MakeOrd[T any](c func(x, y T) int, e func(x, y T) bool) Ord[T] { + return ord[T]{c: c, e: e} +} + +// MakeOrd creates an instance of an Ord from a compare function +func FromCompare[T any](compare func(T, T) int) Ord[T] { + return MakeOrd(compare, func(x, y T) bool { + return compare(x, y) == 0 + }) +} + +// Reverse creates an inverted ordering +func Reverse[T any](o Ord[T]) Ord[T] { + return MakeOrd(func(y, x T) int { + return o.Compare(x, y) + }, o.Equals) +} + +// Contramap creates an ordering under a transformation function +func Contramap[A, B any](f func(B) A) func(Ord[A]) Ord[B] { + return func(o Ord[A]) Ord[B] { + return MakeOrd(func(x, y B) int { + return o.Compare(f(x), f(y)) + }, func(x, y B) bool { + return o.Equals(f(x), f(y)) + }) + } +} + +// Min takes the minimum of two values. If they are considered equal, the first argument is chosen +func Min[A any](o Ord[A]) func(A, A) A { + return func(a, b A) A { + if o.Compare(a, b) < 1 { + return a + } + return b + } +} + +// Max takes the maximum of two values. If they are considered equal, the first argument is chosen +func Max[A any](o Ord[A]) func(A, A) A { + return func(a, b A) A { + if o.Compare(a, b) >= 0 { + return a + } + return b + } +} + +// Clamp clamps a value between a minimum and a maximum +func Clamp[A any](o Ord[A]) func(A, A) func(A) A { + return func(low, hi A) func(A) A { + clow := F.Bind2nd(o.Compare, low) + chi := F.Bind2nd(o.Compare, hi) + return func(a A) A { + if clow(a) <= 0 { + return low + } + if chi(a) >= 0 { + return hi + } + return a + } + } +} + +func strictCompare[A C.Ordered](a, b A) int { + if a < b { + return -1 + } else if a > b { + return +1 + } else { + return 0 + } +} + +func strictEq[A comparable](a, b A) bool { + return a == b +} + +// FromStrictCompare implements the ordering based on the built in native order +func FromStrictCompare[A C.Ordered]() Ord[A] { + return MakeOrd(strictCompare[A], strictEq[A]) +} + +// Lt tests whether one value is strictly less than another +func Lt[A any](o Ord[A]) func(A) func(A) bool { + return func(second A) func(A) bool { + return func(first A) bool { + return o.Compare(first, second) < 0 + } + } +} + +// Leq Tests whether one value is less or equal than another +func Leq[A any](O Ord[A]) func(A) func(A) bool { + return func(second A) func(A) bool { + return func(first A) bool { + return O.Compare(first, second) <= 0 + } + } +} + +/** + * Test whether one value is strictly greater than another + */ +func Gt[A any](O Ord[A]) func(A) func(A) bool { + return func(second A) func(A) bool { + return func(first A) bool { + return O.Compare(first, second) > 0 + } + } +} + +// Geq tests whether one value is greater or equal than another +func Geq[A any](O Ord[A]) func(A) func(A) bool { + return func(second A) func(A) bool { + return func(first A) bool { + return O.Compare(first, second) >= 0 + } + } +} + +// Between tests whether a value is between a minimum (inclusive) and a maximum (exclusive) +func Between[A any](O Ord[A]) func(A, A) func(A) bool { + lt := Lt(O) + geq := Geq(O) + return func(lo, hi A) func(A) bool { + // returns the predicate + return P.And(lt(hi))(geq(lo)) + } +} diff --git a/v2/pair/eq.go b/v2/pair/eq.go new file mode 100644 index 0000000..ea811b5 --- /dev/null +++ b/v2/pair/eq.go @@ -0,0 +1,33 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pair + +import ( + EQ "github.com/IBM/fp-go/v2/eq" +) + +// Constructs an equal predicate for an `Either` +func Eq[A, B any](a EQ.Eq[A], b EQ.Eq[B]) EQ.Eq[Pair[A, B]] { + return EQ.FromEquals(func(l, r Pair[A, B]) bool { + return a.Equals(Head(l), Head(r)) && b.Equals(Tail(l), Tail(r)) + }) + +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[A, B comparable]() EQ.Eq[Pair[A, B]] { + return Eq(EQ.FromStrictEquals[A](), EQ.FromStrictEquals[B]()) +} diff --git a/v2/pair/generic/sequence.go b/v2/pair/generic/sequence.go new file mode 100644 index 0000000..c45f673 --- /dev/null +++ b/v2/pair/generic/sequence.go @@ -0,0 +1,71 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + P "github.com/IBM/fp-go/v2/pair" +) + +// SequencePair is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Pair] of higher higher kinded types and returns a higher kinded type of a [Pair] with the resolved values. +func SequencePair[ + MAP ~func(func(T1) func(T2) P.Pair[T1, T2]) func(HKT_T1) HKT_F_T2, + AP1 ~func(HKT_T2) func(HKT_F_T2) HKT_PAIR, + T1, + T2, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_F_T2, // HKT[func(T2) P.Pair[T1, T2]] + HKT_PAIR any, // HKT[Pair[T1, T2]] +]( + fmap MAP, + fap1 AP1, + t P.Pair[HKT_T1, HKT_T2], +) HKT_PAIR { + return F.Pipe2( + P.Head(t), + fmap(F.Curry2(P.MakePair[T1, T2])), + fap1(P.Tail(t)), + ) +} + +// TraversePair is a utility function used to implement the sequence operation for higher kinded types based only on map and ap. +// The function takes a [Pair] of base types and 2 functions that transform these based types into higher higher kinded types. It returns a higher kinded type of a [Pair] with the resolved values. +func TraversePair[ + MAP ~func(func(T1) func(T2) P.Pair[T1, T2]) func(HKT_T1) HKT_F_T2, + AP1 ~func(HKT_T2) func(HKT_F_T2) HKT_PAIR, + F1 ~func(A1) HKT_T1, + F2 ~func(A2) HKT_T2, + A1, T1, + A2, T2, + HKT_T1, // HKT[T1] + HKT_T2, // HKT[T2] + HKT_F_T2, // HKT[func(T2) P.Pair[T1, T2]] + HKT_PAIR any, // HKT[Pair[T1, T2]] +]( + fmap MAP, + fap1 AP1, + f1 F1, + f2 F2, + t P.Pair[A1, A2], +) HKT_PAIR { + return F.Pipe2( + f1(P.Head(t)), + fmap(F.Curry2(P.MakePair[T1, T2])), + fap1(f2(P.Tail(t))), + ) +} diff --git a/v2/pair/monad.go b/v2/pair/monad.go new file mode 100644 index 0000000..cdb7198 --- /dev/null +++ b/v2/pair/monad.go @@ -0,0 +1,193 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pair + +import ( + "github.com/IBM/fp-go/v2/internal/applicative" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" + M "github.com/IBM/fp-go/v2/monoid" + Sg "github.com/IBM/fp-go/v2/semigroup" +) + +type ( + pairPointedHead[A, B any] struct { + m M.Monoid[B] + } + + pairFunctorHead[A, B, A1 any] struct { + } + + pairApplicativeHead[A, B, A1 any] struct { + s Sg.Semigroup[B] + m M.Monoid[B] + } + + pairMonadHead[A, B, A1 any] struct { + s Sg.Semigroup[B] + m M.Monoid[B] + } + + pairPointedTail[A, B any] struct { + m M.Monoid[A] + } + + pairFunctorTail[A, B, B1 any] struct { + } + + pairApplicativeTail[A, B, B1 any] struct { + s Sg.Semigroup[A] + m M.Monoid[A] + } + + pairMonadTail[A, B, B1 any] struct { + s Sg.Semigroup[A] + m M.Monoid[A] + } +) + +func (o *pairMonadHead[A, B, A1]) Of(a A) Pair[A, B] { + return MakePair(a, o.m.Empty()) +} + +func (o *pairMonadHead[A, B, A1]) Map(f func(A) A1) func(Pair[A, B]) Pair[A1, B] { + return Map[B](f) +} + +func (o *pairMonadHead[A, B, A1]) Chain(f func(A) Pair[A1, B]) func(Pair[A, B]) Pair[A1, B] { + return Chain[B, A, A1](o.s, f) +} + +func (o *pairMonadHead[A, B, A1]) Ap(fa Pair[A, B]) func(Pair[func(A) A1, B]) Pair[A1, B] { + return Ap[B, A, A1](o.s, fa) +} + +func (o *pairPointedHead[A, B]) Of(a A) Pair[A, B] { + return MakePair(a, o.m.Empty()) +} + +func (o *pairFunctorHead[A, B, A1]) Map(f func(A) A1) func(Pair[A, B]) Pair[A1, B] { + return Map[B, A, A1](f) +} + +func (o *pairApplicativeHead[A, B, A1]) Map(f func(A) A1) func(Pair[A, B]) Pair[A1, B] { + return Map[B, A, A1](f) +} + +func (o *pairApplicativeHead[A, B, A1]) Ap(fa Pair[A, B]) func(Pair[func(A) A1, B]) Pair[A1, B] { + return Ap[B, A, A1](o.s, fa) +} + +func (o *pairApplicativeHead[A, B, A1]) Of(a A) Pair[A, B] { + return MakePair(a, o.m.Empty()) +} + +// Monad implements the monadic operations for [Pair] +func Monad[A, B, A1 any](m M.Monoid[B]) monad.Monad[A, A1, Pair[A, B], Pair[A1, B], Pair[func(A) A1, B]] { + return &pairMonadHead[A, B, A1]{s: M.ToSemigroup(m), m: m} +} + +// Pointed implements the pointed operations for [Pair] +func Pointed[A, B any](m M.Monoid[B]) pointed.Pointed[A, Pair[A, B]] { + return &pairPointedHead[A, B]{m: m} +} + +// Functor implements the functor operations for [Pair] +func Functor[A, B, A1 any]() functor.Functor[A, A1, Pair[A, B], Pair[A1, B]] { + return &pairFunctorHead[A, B, A1]{} +} + +// Applicative implements the applicative operations for [Pair] +func Applicative[A, B, A1 any](m M.Monoid[B]) applicative.Applicative[A, A1, Pair[A, B], Pair[A1, B], Pair[func(A) A1, B]] { + return &pairApplicativeHead[A, B, A1]{s: M.ToSemigroup(m), m: m} +} + +// MonadHead implements the monadic operations for [Pair] +func MonadHead[A, B, A1 any](m M.Monoid[B]) monad.Monad[A, A1, Pair[A, B], Pair[A1, B], Pair[func(A) A1, B]] { + return Monad[A, B, A1](m) +} + +// PointedHead implements the pointed operations for [Pair] +func PointedHead[A, B any](m M.Monoid[B]) pointed.Pointed[A, Pair[A, B]] { + return PointedHead[A, B](m) +} + +// FunctorHead implements the functor operations for [Pair] +func FunctorHead[A, B, A1 any]() functor.Functor[A, A1, Pair[A, B], Pair[A1, B]] { + return Functor[A, B, A1]() +} + +// ApplicativeHead implements the applicative operations for [Pair] +func ApplicativeHead[A, B, A1 any](m M.Monoid[B]) applicative.Applicative[A, A1, Pair[A, B], Pair[A1, B], Pair[func(A) A1, B]] { + return Applicative[A, B, A1](m) +} + +func (o *pairMonadTail[A, B, B1]) Of(b B) Pair[A, B] { + return MakePair(o.m.Empty(), b) +} + +func (o *pairMonadTail[A, B, B1]) Map(f func(B) B1) func(Pair[A, B]) Pair[A, B1] { + return MapTail[A, B, B1](f) +} + +func (o *pairMonadTail[A, B, B1]) Chain(f func(B) Pair[A, B1]) func(Pair[A, B]) Pair[A, B1] { + return ChainTail[A, B, B1](o.s, f) +} + +func (o *pairMonadTail[A, B, B1]) Ap(fa Pair[A, B]) func(Pair[A, func(B) B1]) Pair[A, B1] { + return ApTail[A, B, B1](o.s, fa) +} + +func (o *pairPointedTail[A, B]) Of(b B) Pair[A, B] { + return MakePair(o.m.Empty(), b) +} + +func (o *pairFunctorTail[A, B, B1]) Map(f func(B) B1) func(Pair[A, B]) Pair[A, B1] { + return MapTail[A, B, B1](f) +} + +func (o *pairApplicativeTail[A, B, B1]) Map(f func(B) B1) func(Pair[A, B]) Pair[A, B1] { + return MapTail[A, B, B1](f) +} + +func (o *pairApplicativeTail[A, B, B1]) Ap(fa Pair[A, B]) func(Pair[A, func(B) B1]) Pair[A, B1] { + return ApTail[A, B, B1](o.s, fa) +} + +func (o *pairApplicativeTail[A, B, B1]) Of(b B) Pair[A, B] { + return MakePair(o.m.Empty(), b) +} + +// MonadTail implements the monadic operations for [Pair] +func MonadTail[B, A, B1 any](m M.Monoid[A]) monad.Monad[B, B1, Pair[A, B], Pair[A, B1], Pair[A, func(B) B1]] { + return &pairMonadTail[A, B, B1]{s: M.ToSemigroup(m), m: m} +} + +// PointedTail implements the pointed operations for [Pair] +func PointedTail[B, A any](m M.Monoid[A]) pointed.Pointed[B, Pair[A, B]] { + return &pairPointedTail[A, B]{m: m} +} + +// FunctorTail implements the functor operations for [Pair] +func FunctorTail[B, A, B1 any]() functor.Functor[B, B1, Pair[A, B], Pair[A, B1]] { + return &pairFunctorTail[A, B, B1]{} +} + +// ApplicativeTail implements the applicative operations for [Pair] +func ApplicativeTail[B, A, B1 any](m M.Monoid[A]) applicative.Applicative[B, B1, Pair[A, B], Pair[A, B1], Pair[A, func(B) B1]] { + return &pairApplicativeTail[A, B, B1]{s: M.ToSemigroup(m), m: m} +} diff --git a/v2/pair/pair.go b/v2/pair/pair.go new file mode 100644 index 0000000..7d4b481 --- /dev/null +++ b/v2/pair/pair.go @@ -0,0 +1,231 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pair + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" + Sg "github.com/IBM/fp-go/v2/semigroup" + T "github.com/IBM/fp-go/v2/tuple" +) + +type ( + pair struct { + h, t any + } + + // Pair defines a data structure that holds two strongly typed values + Pair[A, B any] pair +) + +// String prints some debug info for the object +// +// go:noinline +func pairString(s *pair) string { + return fmt.Sprintf("Pair[%T, %T](%v, %v)", s.h, s.t, s.h, s.t) +} + +// Format prints some debug info for the object +// +// go:noinline +func pairFormat(e *pair, f fmt.State, c rune) { + switch c { + case 's': + fmt.Fprint(f, pairString(e)) + default: + fmt.Fprint(f, pairString(e)) + } +} + +// String prints some debug info for the object +func (s Pair[A, B]) String() string { + return pairString((*pair)(&s)) +} + +// Format prints some debug info for the object +func (s Pair[A, B]) Format(f fmt.State, c rune) { + pairFormat((*pair)(&s), f, c) +} + +// Of creates a [Pair] with the same value to to both fields +func Of[A any](value A) Pair[A, A] { + return Pair[A, A]{h: value, t: value} +} + +// FromTuple creates a [Pair] from a [T.Tuple2] +func FromTuple[A, B any](t T.Tuple2[A, B]) Pair[A, B] { + return Pair[A, B]{h: t.F1, t: t.F2} +} + +// ToTuple creates a [T.Tuple2] from a [Pair] +func ToTuple[A, B any](t Pair[A, B]) T.Tuple2[A, B] { + return T.MakeTuple2(Head(t), Tail(t)) +} + +// MakePair creates a [Pair] from two values +func MakePair[A, B any](a A, b B) Pair[A, B] { + return Pair[A, B]{h: a, t: b} +} + +// Head returns the head value of the pair +func Head[A, B any](fa Pair[A, B]) A { + return fa.h.(A) +} + +// Tail returns the head value of the pair +func Tail[A, B any](fa Pair[A, B]) B { + return fa.t.(B) +} + +// MonadMapHead maps the head value +func MonadMapHead[B, A, A1 any](fa Pair[A, B], f func(A) A1) Pair[A1, B] { + return Pair[A1, B]{f(Head(fa)), fa.t} +} + +// MonadMap maps the head value +func MonadMap[B, A, A1 any](fa Pair[A, B], f func(A) A1) Pair[A1, B] { + return MonadMapHead(fa, f) +} + +// MonadMapTail maps the Tail value +func MonadMapTail[A, B, B1 any](fa Pair[A, B], f func(B) B1) Pair[A, B1] { + return Pair[A, B1]{fa.h, f(Tail(fa))} +} + +// MonadBiMap maps both values +func MonadBiMap[A, B, A1, B1 any](fa Pair[A, B], f func(A) A1, g func(B) B1) Pair[A1, B1] { + return Pair[A1, B1]{f(Head(fa)), g(Tail(fa))} +} + +// Map maps the head value +func Map[B, A, A1 any](f func(A) A1) func(Pair[A, B]) Pair[A1, B] { + return MapHead[B, A, A1](f) +} + +// MapHead maps the head value +func MapHead[B, A, A1 any](f func(A) A1) func(Pair[A, B]) Pair[A1, B] { + return F.Bind2nd(MonadMapHead[B, A, A1], f) +} + +// MapTail maps the Tail value +func MapTail[A, B, B1 any](f func(B) B1) func(Pair[A, B]) Pair[A, B1] { + return F.Bind2nd(MonadMapTail[A, B, B1], f) +} + +// BiMap maps both values +func BiMap[A, B, A1, B1 any](f func(A) A1, g func(B) B1) func(Pair[A, B]) Pair[A1, B1] { + return func(fa Pair[A, B]) Pair[A1, B1] { + return MonadBiMap(fa, f, g) + } +} + +// MonadChainHead chains on the head value +func MonadChainHead[B, A, A1 any](sg Sg.Semigroup[B], fa Pair[A, B], f func(A) Pair[A1, B]) Pair[A1, B] { + fb := f(Head(fa)) + return Pair[A1, B]{fb.h, sg.Concat(Tail(fa), Tail(fb))} +} + +// MonadChainTail chains on the Tail value +func MonadChainTail[A, B, B1 any](sg Sg.Semigroup[A], fb Pair[A, B], f func(B) Pair[A, B1]) Pair[A, B1] { + fa := f(Tail(fb)) + return Pair[A, B1]{sg.Concat(Head(fb), Head(fa)), fa.t} +} + +// MonadChain chains on the head value +func MonadChain[B, A, A1 any](sg Sg.Semigroup[B], fa Pair[A, B], f func(A) Pair[A1, B]) Pair[A1, B] { + return MonadChainHead(sg, fa, f) +} + +// ChainHead chains on the head value +func ChainHead[B, A, A1 any](sg Sg.Semigroup[B], f func(A) Pair[A1, B]) func(Pair[A, B]) Pair[A1, B] { + return func(fa Pair[A, B]) Pair[A1, B] { + return MonadChainHead(sg, fa, f) + } +} + +// ChainTail chains on the Tail value +func ChainTail[A, B, B1 any](sg Sg.Semigroup[A], f func(B) Pair[A, B1]) func(Pair[A, B]) Pair[A, B1] { + return func(fa Pair[A, B]) Pair[A, B1] { + return MonadChainTail(sg, fa, f) + } +} + +// Chain chains on the head value +func Chain[B, A, A1 any](sg Sg.Semigroup[B], f func(A) Pair[A1, B]) func(Pair[A, B]) Pair[A1, B] { + return ChainHead[B, A, A1](sg, f) +} + +// MonadApHead applies on the head value +func MonadApHead[B, A, A1 any](sg Sg.Semigroup[B], faa Pair[func(A) A1, B], fa Pair[A, B]) Pair[A1, B] { + return Pair[A1, B]{Head(faa)(Head(fa)), sg.Concat(Tail(fa), Tail(faa))} +} + +// MonadApTail applies on the Tail value +func MonadApTail[A, B, B1 any](sg Sg.Semigroup[A], fbb Pair[A, func(B) B1], fb Pair[A, B]) Pair[A, B1] { + return Pair[A, B1]{sg.Concat(Head(fb), Head(fbb)), Tail(fbb)(Tail(fb))} +} + +// MonadAp applies on the head value +func MonadAp[B, A, A1 any](sg Sg.Semigroup[B], faa Pair[func(A) A1, B], fa Pair[A, B]) Pair[A1, B] { + return MonadApHead(sg, faa, fa) +} + +// ApHead applies on the head value +func ApHead[B, A, A1 any](sg Sg.Semigroup[B], fa Pair[A, B]) func(Pair[func(A) A1, B]) Pair[A1, B] { + return func(faa Pair[func(A) A1, B]) Pair[A1, B] { + return MonadApHead(sg, faa, fa) + } +} + +// ApTail applies on the Tail value +func ApTail[A, B, B1 any](sg Sg.Semigroup[A], fb Pair[A, B]) func(Pair[A, func(B) B1]) Pair[A, B1] { + return func(fbb Pair[A, func(B) B1]) Pair[A, B1] { + return MonadApTail(sg, fbb, fb) + } +} + +// Ap applies on the head value +func Ap[B, A, A1 any](sg Sg.Semigroup[B], fa Pair[A, B]) func(Pair[func(A) A1, B]) Pair[A1, B] { + return ApHead[B, A, A1](sg, fa) +} + +// Swap swaps the two channels +func Swap[A, B any](fa Pair[A, B]) Pair[B, A] { + return MakePair(Tail(fa), Head(fa)) +} + +// Paired converts a function with 2 parameters into a function taking a [Pair] +// The inverse function is [Unpaired] +func Paired[F ~func(T1, T2) R, T1, T2, R any](f F) func(Pair[T1, T2]) R { + return func(t Pair[T1, T2]) R { + return f(Head(t), Tail(t)) + } +} + +// Unpaired converts a function with a [Pair] parameter into a function with 2 parameters +// The inverse function is [Paired] +func Unpaired[F ~func(Pair[T1, T2]) R, T1, T2, R any](f F) func(T1, T2) R { + return func(t1 T1, t2 T2) R { + return f(MakePair(t1, t2)) + } +} + +func Merge[F ~func(B) func(A) R, A, B, R any](f F) func(Pair[A, B]) R { + return func(p Pair[A, B]) R { + return f(Tail(p))(Head(p)) + } +} diff --git a/v2/pair/testing/laws.go b/v2/pair/testing/laws.go new file mode 100644 index 0000000..b2ac246 --- /dev/null +++ b/v2/pair/testing/laws.go @@ -0,0 +1,155 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" + P "github.com/IBM/fp-go/v2/pair" + + M "github.com/IBM/fp-go/v2/monoid" +) + +// AssertLaws asserts the apply monad laws for the [P.Pair] monad +func assertLawsHead[E, A, B, C any](t *testing.T, + m M.Monoid[E], + + eqe EQ.Eq[E], + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + fofc := P.Pointed[C](m) + fofaa := P.Pointed[func(A) A](m) + fofbc := P.Pointed[func(B) C](m) + fofabb := P.Pointed[func(func(A) B) B](m) + + fmap := P.Functor[func(B) C, E, func(func(A) B) func(A) C]() + + fapabb := P.Applicative[func(A) B, E, B](m) + fapabac := P.Applicative[func(A) B, E, func(A) C](m) + + maa := P.Monad[A, E, A](m) + mab := P.Monad[A, E, B](m) + mac := P.Monad[A, E, C](m) + mbc := P.Monad[B, E, C](m) + + return L.MonadAssertLaws(t, + P.Eq(eqa, eqe), + P.Eq(eqb, eqe), + P.Eq(eqc, eqe), + + fofc, + fofaa, + fofbc, + fofabb, + + fmap, + + fapabb, + fapabac, + + maa, + mab, + mac, + mbc, + + ab, + bc, + ) + +} + +// AssertLaws asserts the apply monad laws for the [P.Pair] monad +func assertLawsTail[E, A, B, C any](t *testing.T, + m M.Monoid[E], + + eqe EQ.Eq[E], + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(a A) bool { + + fofc := P.PointedTail[C](m) + fofaa := P.PointedTail[func(A) A](m) + fofbc := P.PointedTail[func(B) C](m) + fofabb := P.PointedTail[func(func(A) B) B](m) + + fmap := P.FunctorTail[func(B) C, E, func(func(A) B) func(A) C]() + + fapabb := P.ApplicativeTail[func(A) B, E, B](m) + fapabac := P.ApplicativeTail[func(A) B, E, func(A) C](m) + + maa := P.MonadTail[A, E, A](m) + mab := P.MonadTail[A, E, B](m) + mac := P.MonadTail[A, E, C](m) + mbc := P.MonadTail[B, E, C](m) + + return L.MonadAssertLaws(t, + P.Eq(eqe, eqa), + P.Eq(eqe, eqb), + P.Eq(eqe, eqc), + + fofc, + fofaa, + fofbc, + fofabb, + + fmap, + + fapabb, + fapabac, + + maa, + mab, + mac, + mbc, + + ab, + bc, + ) + +} + +// AssertLaws asserts the apply monad laws for the [P.Pair] monad +func AssertLaws[E, A, B, C any](t *testing.T, + m M.Monoid[E], + + eqe EQ.Eq[E], + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, +) func(A) bool { + + head := assertLawsHead(t, m, eqe, eqa, eqb, eqc, ab, bc) + tail := assertLawsHead(t, m, eqe, eqa, eqb, eqc, ab, bc) + + return func(a A) bool { + return head(a) && tail(a) + } +} diff --git a/v2/pair/testing/laws_test.go b/v2/pair/testing/laws_test.go new file mode 100644 index 0000000..675d20e --- /dev/null +++ b/v2/pair/testing/laws_test.go @@ -0,0 +1,51 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqe := EQ.FromStrictEquals[string]() + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + m := S.Monoid + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, m, eqe, eqa, eqb, eqc, ab, bc) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/predicate/bool.go b/v2/predicate/bool.go new file mode 100644 index 0000000..864b772 --- /dev/null +++ b/v2/predicate/bool.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package predicate + +func Not[A any](predicate func(A) bool) func(A) bool { + return func(a A) bool { + return !predicate(a) + } +} + +// And creates a predicate that combines other predicates via && +func And[A any](second func(A) bool) func(func(A) bool) func(A) bool { + return func(first func(A) bool) func(A) bool { + return func(a A) bool { + return first(a) && second(a) + } + } +} + +// Or creates a predicate that combines other predicates via || +func Or[A any](second func(A) bool) func(func(A) bool) func(A) bool { + return func(first func(A) bool) func(A) bool { + return func(a A) bool { + return first(a) || second(a) + } + } +} diff --git a/v2/predicate/contramap.go b/v2/predicate/contramap.go new file mode 100644 index 0000000..ea7c999 --- /dev/null +++ b/v2/predicate/contramap.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package predicate + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// ContraMap creates a predicate from an existing predicate given a mapping function +func ContraMap[A, B any](f func(B) A) func(func(A) bool) func(B) bool { + return func(pred func(A) bool) func(B) bool { + return F.Flow2( + f, + pred, + ) + } +} diff --git a/v2/predicate/monoid.go b/v2/predicate/monoid.go new file mode 100644 index 0000000..77e44c4 --- /dev/null +++ b/v2/predicate/monoid.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package predicate + +import ( + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// SemigroupAny combines predicates via || +func SemigroupAny[A any]() S.Semigroup[func(A) bool] { + return S.MakeSemigroup(func(first func(A) bool, second func(A) bool) func(A) bool { + return F.Pipe1( + first, + Or(second), + ) + }) +} + +// SemigroupAll combines predicates via && +func SemigroupAll[A any]() S.Semigroup[func(A) bool] { + return S.MakeSemigroup(func(first func(A) bool, second func(A) bool) func(A) bool { + return F.Pipe1( + first, + And(second), + ) + }) +} + +// MonoidAny combines predicates via || +func MonoidAny[A any]() S.Semigroup[func(A) bool] { + return M.MakeMonoid( + SemigroupAny[A]().Concat, + F.Constant1[A](false), + ) +} + +// MonoidAll combines predicates via && +func MonoidAll[A any]() S.Semigroup[func(A) bool] { + return M.MakeMonoid( + SemigroupAll[A]().Concat, + F.Constant1[A](true), + ) +} diff --git a/v2/reader/array.go b/v2/reader/array.go new file mode 100644 index 0000000..dcacc18 --- /dev/null +++ b/v2/reader/array.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + G "github.com/IBM/fp-go/v2/reader/generic" +) + +// TraverseArray transforms an array +func TraverseArray[R, A, B any](f func(A) Reader[R, B]) func([]A) Reader[R, []B] { + return G.TraverseArray[Reader[R, B], Reader[R, []B], []A](f) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[R, A, B any](f func(int, A) Reader[R, B]) func([]A) Reader[R, []B] { + return G.TraverseArrayWithIndex[Reader[R, B], Reader[R, []B], []A](f) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[R, A any](ma []Reader[R, A]) Reader[R, []A] { + return G.SequenceArray[Reader[R, A], Reader[R, []A]](ma) +} diff --git a/v2/reader/array_test.go b/v2/reader/array_test.go new file mode 100644 index 0000000..7d23470 --- /dev/null +++ b/v2/reader/array_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + "context" + "testing" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestSequenceArray(t *testing.T) { + + n := 10 + + readers := A.MakeBy(n, Of[context.Context, int]) + exp := A.MakeBy(n, F.Identity[int]) + + g := F.Pipe1( + readers, + SequenceArray[context.Context, int], + ) + + assert.Equal(t, exp, g(context.Background())) +} diff --git a/v2/reader/bind.go b/v2/reader/bind.go new file mode 100644 index 0000000..00fee33 --- /dev/null +++ b/v2/reader/bind.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + G "github.com/IBM/fp-go/v2/reader/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[R, S any]( + empty S, +) Reader[R, S] { + return G.Do[Reader[R, S], R, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) Reader[R, T], +) func(Reader[R, S1]) Reader[R, S2] { + return G.Bind[Reader[R, S1], Reader[R, S2], Reader[R, T], R, S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(Reader[R, S1]) Reader[R, S2] { + return G.Let[Reader[R, S1], Reader[R, S2], R, S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[R, S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(Reader[R, S1]) Reader[R, S2] { + return G.LetTo[Reader[R, S1], Reader[R, S2], R, S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[R, S1, T any]( + setter func(T) S1, +) func(Reader[R, T]) Reader[R, S1] { + return G.BindTo[Reader[R, S1], Reader[R, T], R, S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa Reader[R, T], +) func(Reader[R, S1]) Reader[R, S2] { + return G.ApS[Reader[R, S1], Reader[R, S2], Reader[R, T], R, S1, S2, T](setter, fa) +} diff --git a/v2/reader/bind_test.go b/v2/reader/bind_test.go new file mode 100644 index 0000000..0a78df6 --- /dev/null +++ b/v2/reader/bind_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + "context" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) Reader[context.Context, string] { + return Of[context.Context]("Doe") +} + +func getGivenName(s utils.WithLastName) Reader[context.Context, string] { + return Of[context.Context]("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do[context.Context](utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map[context.Context](utils.GetFullName), + ) + + assert.Equal(t, res(context.Background()), "John Doe") +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do[context.Context](utils.Empty), + ApS(utils.SetLastName, Of[context.Context]("Doe")), + ApS(utils.SetGivenName, Of[context.Context]("John")), + Map[context.Context](utils.GetFullName), + ) + + assert.Equal(t, res(context.Background()), "John Doe") +} diff --git a/v2/reader/curry.go b/v2/reader/curry.go new file mode 100644 index 0000000..b5f2898 --- /dev/null +++ b/v2/reader/curry.go @@ -0,0 +1,64 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + G "github.com/IBM/fp-go/v2/reader/generic" +) + +// these functions curry a golang function with the context as the firsr parameter into a reader with the context as the last parameter, which +// is a equivalent to a function returning a reader of that context +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func Curry0[R, A any](f func(R) A) Reader[R, A] { + return G.Curry0[Reader[R, A]](f) +} + +func Curry1[R, T1, A any](f func(R, T1) A) func(T1) Reader[R, A] { + return G.Curry1[Reader[R, A]](f) +} + +func Curry2[R, T1, T2, A any](f func(R, T1, T2) A) func(T1) func(T2) Reader[R, A] { + return G.Curry2[Reader[R, A]](f) +} + +func Curry3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) A) func(T1) func(T2) func(T3) Reader[R, A] { + return G.Curry3[Reader[R, A]](f) +} + +func Curry4[R, T1, T2, T3, T4, A any](f func(R, T1, T2, T3, T4) A) func(T1) func(T2) func(T3) func(T4) Reader[R, A] { + return G.Curry4[Reader[R, A]](f) +} + +func Uncurry0[R, A any](f Reader[R, A]) func(R) A { + return G.Uncurry0(f) +} + +func Uncurry1[R, T1, A any](f func(T1) Reader[R, A]) func(R, T1) A { + return G.Uncurry1(f) +} + +func Uncurry2[R, T1, T2, A any](f func(T1) func(T2) Reader[R, A]) func(R, T1, T2) A { + return G.Uncurry2(f) +} + +func Uncurry3[R, T1, T2, T3, A any](f func(T1) func(T2) func(T3) Reader[R, A]) func(R, T1, T2, T3) A { + return G.Uncurry3(f) +} + +func Uncurry4[R, T1, T2, T3, T4, A any](f func(T1) func(T2) func(T3) func(T4) Reader[R, A]) func(R, T1, T2, T3, T4) A { + return G.Uncurry4(f) +} diff --git a/v2/reader/doc.go b/v2/reader/doc.go new file mode 100644 index 0000000..5a64eed --- /dev/null +++ b/v2/reader/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +//go:generate go run .. reader --count 10 --filename gen.go diff --git a/v2/reader/gen.go b/v2/reader/gen.go new file mode 100644 index 0000000..19196f0 --- /dev/null +++ b/v2/reader/gen.go @@ -0,0 +1,75 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:26.0521202 +0100 CET m=+0.018000401 + +package reader + +import ( + G "github.com/IBM/fp-go/v2/reader/generic" +) + +// From0 converts a function with 1 parameters returning a [R] into a function with 0 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From0[F ~func(C) R, C, R any](f F) func() Reader[C, R] { + return G.From0[Reader[C, R]](f) +} + +// From1 converts a function with 2 parameters returning a [R] into a function with 1 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From1[F ~func(C, T0) R, T0, C, R any](f F) func(T0) Reader[C, R] { + return G.From1[Reader[C, R]](f) +} + +// From2 converts a function with 3 parameters returning a [R] into a function with 2 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From2[F ~func(C, T0, T1) R, T0, T1, C, R any](f F) func(T0, T1) Reader[C, R] { + return G.From2[Reader[C, R]](f) +} + +// From3 converts a function with 4 parameters returning a [R] into a function with 3 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From3[F ~func(C, T0, T1, T2) R, T0, T1, T2, C, R any](f F) func(T0, T1, T2) Reader[C, R] { + return G.From3[Reader[C, R]](f) +} + +// From4 converts a function with 5 parameters returning a [R] into a function with 4 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From4[F ~func(C, T0, T1, T2, T3) R, T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) Reader[C, R] { + return G.From4[Reader[C, R]](f) +} + +// From5 converts a function with 6 parameters returning a [R] into a function with 5 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From5[F ~func(C, T0, T1, T2, T3, T4) R, T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) Reader[C, R] { + return G.From5[Reader[C, R]](f) +} + +// From6 converts a function with 7 parameters returning a [R] into a function with 6 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From6[F ~func(C, T0, T1, T2, T3, T4, T5) R, T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) Reader[C, R] { + return G.From6[Reader[C, R]](f) +} + +// From7 converts a function with 8 parameters returning a [R] into a function with 7 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) R, T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) Reader[C, R] { + return G.From7[Reader[C, R]](f) +} + +// From8 converts a function with 9 parameters returning a [R] into a function with 8 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) R, T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) Reader[C, R] { + return G.From8[Reader[C, R]](f) +} + +// From9 converts a function with 10 parameters returning a [R] into a function with 9 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) R, T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) Reader[C, R] { + return G.From9[Reader[C, R]](f) +} + +// From10 converts a function with 11 parameters returning a [R] into a function with 10 parameters returning a [Reader[C, R]] +// The first parameter is considered to be the context [C] of the reader +func From10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) R, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) Reader[C, R] { + return G.From10[Reader[C, R]](f) +} diff --git a/v2/reader/generic/array.go b/v2/reader/generic/array.go new file mode 100644 index 0000000..710f024 --- /dev/null +++ b/v2/reader/generic/array.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" +) + +// TraverseArray transforms an array +func MonadTraverseArray[GB ~func(R) B, GBS ~func(R) BBS, AAS ~[]A, BBS ~[]B, R, A, B any](tas AAS, f func(A) GB) GBS { + return RA.MonadTraverse[AAS]( + Of[GBS, R, BBS], + Map[GBS, func(R) func(B) BBS, R, BBS, func(B) BBS], + Ap[GB, GBS, func(R) func(B) BBS, R, B, BBS], + tas, f, + ) +} + +// TraverseArray transforms an array +func TraverseArray[GB ~func(R) B, GBS ~func(R) BBS, AAS ~[]A, BBS ~[]B, R, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, R, BBS], + Map[GBS, func(R) func(B) BBS, R, BBS, func(B) BBS], + Ap[GB, GBS, func(R) func(B) BBS, R, B, BBS], + f, + ) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[GB ~func(R) B, GBS ~func(R) BBS, AAS ~[]A, BBS ~[]B, R, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, R, BBS], + Map[GBS, func(R) func(B) BBS, R, BBS, func(B) BBS], + Ap[GB, GBS, func(R) func(B) BBS, R, B, BBS], + f, + ) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[GA ~func(R) A, GAS ~func(R) AAS, AAS ~[]A, GAAS ~[]GA, R, A any](ma GAAS) GAS { + return MonadTraverseArray[GA, GAS](ma, F.Identity[GA]) +} diff --git a/v2/reader/generic/bind.go b/v2/reader/generic/bind.go new file mode 100644 index 0000000..e53df65 --- /dev/null +++ b/v2/reader/generic/bind.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GS ~func(R) S, R, S any]( + empty S, +) GS { + return Of[GS, R, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GS1 ~func(R) S1, GS2 ~func(R) S2, GT ~func(R) T, R, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GT, +) func(GS1) GS2 { + return C.Bind( + Chain[GS1, GS2, R, S1, S2], + Map[GT, GS2, R, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GS1 ~func(R) S1, GS2 ~func(R) S2, R, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GS1) GS2 { + return F.Let( + Map[GS1, GS2, R, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GS1 ~func(R) S1, GS2 ~func(R) S2, R, S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GS1) GS2 { + return F.LetTo( + Map[GS1, GS2, R, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GS1 ~func(R) S1, GT ~func(R) T, R, S1, T any]( + setter func(T) S1, +) func(GT) GS1 { + return C.BindTo( + Map[GT, GS1, R, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GS1 ~func(R) S1, GS2 ~func(R) S2, GT ~func(R) T, R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa GT, +) func(GS1) GS2 { + return A.ApS( + Ap[GT, GS2, func(R) func(T) S2, R, T, S2], + Map[GS1, func(R) func(T) S2, R, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/reader/generic/curry.go b/v2/reader/generic/curry.go new file mode 100644 index 0000000..44cd264 --- /dev/null +++ b/v2/reader/generic/curry.go @@ -0,0 +1,76 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +// these functions curry a golang function with the context as the firsr parameter into a reader with the context as the last parameter, which +// is a equivalent to a function returning a reader of that context +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func Curry0[GA ~func(R) A, R, A any](f func(R) A) GA { + return MakeReader[GA](f) +} + +func Curry1[GA ~func(R) A, R, T1, A any](f func(R, T1) A) func(T1) GA { + return F.Curry1(From1[GA](f)) +} + +func Curry2[GA ~func(R) A, R, T1, T2, A any](f func(R, T1, T2) A) func(T1) func(T2) GA { + return F.Curry2(From2[GA](f)) +} + +func Curry3[GA ~func(R) A, R, T1, T2, T3, A any](f func(R, T1, T2, T3) A) func(T1) func(T2) func(T3) GA { + return F.Curry3(From3[GA](f)) +} + +func Curry4[GA ~func(R) A, R, T1, T2, T3, T4, A any](f func(R, T1, T2, T3, T4) A) func(T1) func(T2) func(T3) func(T4) GA { + return F.Curry4(From4[GA](f)) +} + +func Uncurry0[GA ~func(R) A, R, A any](f GA) func(R) A { + return f +} + +func Uncurry1[GA ~func(R) A, R, T1, A any](f func(T1) GA) func(R, T1) A { + uc := F.Uncurry1(f) + return func(r R, t1 T1) A { + return uc(t1)(r) + } +} + +func Uncurry2[GA ~func(R) A, R, T1, T2, A any](f func(T1) func(T2) GA) func(R, T1, T2) A { + uc := F.Uncurry2(f) + return func(r R, t1 T1, t2 T2) A { + return uc(t1, t2)(r) + } +} + +func Uncurry3[GA ~func(R) A, R, T1, T2, T3, A any](f func(T1) func(T2) func(T3) GA) func(R, T1, T2, T3) A { + uc := F.Uncurry3(f) + return func(r R, t1 T1, t2 T2, t3 T3) A { + return uc(t1, t2, t3)(r) + } +} + +func Uncurry4[GA ~func(R) A, R, T1, T2, T3, T4, A any](f func(T1) func(T2) func(T3) func(T4) GA) func(R, T1, T2, T3, T4) A { + uc := F.Uncurry4(f) + return func(r R, t1 T1, t2 T2, t3 T3, t4 T4) A { + return uc(t1, t2, t3, t4)(r) + } +} diff --git a/v2/reader/generic/gen.go b/v2/reader/generic/gen.go new file mode 100644 index 0000000..04c7792 --- /dev/null +++ b/v2/reader/generic/gen.go @@ -0,0 +1,114 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:26.0521202 +0100 CET m=+0.018000401 +package generic + +// From0 converts a function with 1 parameters returning a [R] into a function with 0 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From0[GRA ~func(C) R, F ~func(C) R, C, R any](f F) func() GRA { + return func() GRA { + return MakeReader[GRA](func(r C) R { + return f(r) + }) + } +} + +// From1 converts a function with 2 parameters returning a [R] into a function with 1 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From1[GRA ~func(C) R, F ~func(C, T0) R, T0, C, R any](f F) func(T0) GRA { + return func(t0 T0) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0) + }) + } +} + +// From2 converts a function with 3 parameters returning a [R] into a function with 2 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From2[GRA ~func(C) R, F ~func(C, T0, T1) R, T0, T1, C, R any](f F) func(T0, T1) GRA { + return func(t0 T0, t1 T1) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1) + }) + } +} + +// From3 converts a function with 4 parameters returning a [R] into a function with 3 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From3[GRA ~func(C) R, F ~func(C, T0, T1, T2) R, T0, T1, T2, C, R any](f F) func(T0, T1, T2) GRA { + return func(t0 T0, t1 T1, t2 T2) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1, t2) + }) + } +} + +// From4 converts a function with 5 parameters returning a [R] into a function with 4 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From4[GRA ~func(C) R, F ~func(C, T0, T1, T2, T3) R, T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) GRA { + return func(t0 T0, t1 T1, t2 T2, t3 T3) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1, t2, t3) + }) + } +} + +// From5 converts a function with 6 parameters returning a [R] into a function with 5 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From5[GRA ~func(C) R, F ~func(C, T0, T1, T2, T3, T4) R, T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) GRA { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1, t2, t3, t4) + }) + } +} + +// From6 converts a function with 7 parameters returning a [R] into a function with 6 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From6[GRA ~func(C) R, F ~func(C, T0, T1, T2, T3, T4, T5) R, T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) GRA { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1, t2, t3, t4, t5) + }) + } +} + +// From7 converts a function with 8 parameters returning a [R] into a function with 7 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From7[GRA ~func(C) R, F ~func(C, T0, T1, T2, T3, T4, T5, T6) R, T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) GRA { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1, t2, t3, t4, t5, t6) + }) + } +} + +// From8 converts a function with 9 parameters returning a [R] into a function with 8 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From8[GRA ~func(C) R, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) R, T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) GRA { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1, t2, t3, t4, t5, t6, t7) + }) + } +} + +// From9 converts a function with 10 parameters returning a [R] into a function with 9 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From9[GRA ~func(C) R, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) R, T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) GRA { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1, t2, t3, t4, t5, t6, t7, t8) + }) + } +} + +// From10 converts a function with 11 parameters returning a [R] into a function with 10 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func From10[GRA ~func(C) R, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) R, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) GRA { + return func(t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) GRA { + return MakeReader[GRA](func(r C) R { + return f(r, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) + }) + } +} diff --git a/v2/reader/generic/reader.go b/v2/reader/generic/reader.go new file mode 100644 index 0000000..a634277 --- /dev/null +++ b/v2/reader/generic/reader.go @@ -0,0 +1,131 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity/generic" + FC "github.com/IBM/fp-go/v2/internal/functor" + T "github.com/IBM/fp-go/v2/tuple" +) + +// Reader[R, A] = func(R) A + +// MakeReader creates a reader, i.e. a method that accepts a context and that returns a value +func MakeReader[GA ~func(R) A, R, A any](r GA) GA { + return r +} + +// Ask reads the current context +func Ask[GR ~func(R) R, R any]() GR { + return MakeReader(F.Identity[R]) +} + +// Asks projects a value from the global context in a Reader +func Asks[GA ~func(R) A, R, A any](f GA) GA { + return MakeReader(f) +} + +func AsksReader[GA ~func(R) A, R, A any](f func(R) GA) GA { + return MakeReader(func(r R) A { + return f(r)(r) + }) +} + +func MonadMap[GA ~func(E) A, GB ~func(E) B, E, A, B any](fa GA, f func(A) B) GB { + return MakeReader(F.Flow2(fa, f)) +} + +// Map can be used to turn functions `func(A)B` into functions `(fa F[A])F[B]` whose argument and return types +// use the type constructor `F` to represent some computational context. +func Map[GA ~func(E) A, GB ~func(E) B, E, A, B any](f func(A) B) func(GA) GB { + return F.Bind2nd(MonadMap[GA, GB, E, A, B], f) +} + +func MonadAp[GA ~func(R) A, GB ~func(R) B, GAB ~func(R) func(A) B, R, A, B any](fab GAB, fa GA) GB { + return MakeReader(func(r R) B { + return fab(r)(fa(r)) + }) +} + +// Ap applies a function to an argument under a type constructor. +func Ap[GA ~func(R) A, GB ~func(R) B, GAB ~func(R) func(A) B, R, A, B any](fa GA) func(GAB) GB { + return F.Bind2nd(MonadAp[GA, GB, GAB, R, A, B], fa) +} + +func Of[GA ~func(R) A, R, A any](a A) GA { + return F.Constant1[R](a) +} + +func MonadChain[GA ~func(R) A, GB ~func(R) B, R, A, B any](ma GA, f func(A) GB) GB { + return MakeReader(func(r R) B { + return f(ma(r))(r) + }) +} + +// Chain composes computations in sequence, using the return value of one computation to determine the next computation. +func Chain[GA ~func(R) A, GB ~func(R) B, R, A, B any](f func(A) GB) func(GA) GB { + return F.Bind2nd(MonadChain[GA, GB, R, A, B], f) +} + +func Flatten[GA ~func(R) A, GGA ~func(R) GA, R, A any](mma GGA) GA { + return MonadChain(mma, F.Identity[GA]) +} + +func Compose[AB ~func(A) B, BC ~func(B) C, AC ~func(A) C, A, B, C any](ab AB) func(BC) AC { + return func(bc BC) AC { + return F.Flow2(ab, bc) + } +} + +func First[GAB ~func(A) B, GABC ~func(T.Tuple2[A, C]) T.Tuple2[B, C], A, B, C any](pab GAB) GABC { + return MakeReader(func(tac T.Tuple2[A, C]) T.Tuple2[B, C] { + return T.MakeTuple2(pab(tac.F1), tac.F2) + }) +} + +func Second[GBC ~func(B) C, GABC ~func(T.Tuple2[A, B]) T.Tuple2[A, C], A, B, C any](pbc GBC) GABC { + return MakeReader(func(tab T.Tuple2[A, B]) T.Tuple2[A, C] { + return T.MakeTuple2(tab.F1, pbc(tab.F2)) + }) +} + +func Promap[GA ~func(E) A, GB ~func(D) B, E, A, D, B any](f func(D) E, g func(A) B) func(GA) GB { + return func(fea GA) GB { + return MakeReader(F.Flow3(f, fea, g)) + } +} + +// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s +// `contramap`). +func Local[GA1 ~func(R1) A, GA2 ~func(R2) A, R2, R1, A any](f func(R2) R1) func(GA1) GA2 { + return func(r1 GA1) GA2 { + return F.Flow2(f, r1) + } +} + +// Read applies a context to a reader to obtain its value +func Read[GA ~func(E) A, E, A any](e E) func(GA) A { + return I.Ap[GA](e) +} + +func MonadFlap[GAB ~func(R) func(A) B, GB ~func(R) B, R, A, B any](fab GAB, a A) GB { + return FC.MonadFlap(MonadMap[GAB, GB], fab, a) +} + +func Flap[GAB ~func(R) func(A) B, GB ~func(R) B, R, A, B any](a A) func(GAB) GB { + return FC.Flap(Map[GAB, GB], a) +} diff --git a/v2/reader/generic/sequence.go b/v2/reader/generic/sequence.go new file mode 100644 index 0000000..51edb9e --- /dev/null +++ b/v2/reader/generic/sequence.go @@ -0,0 +1,61 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[GA ~func(R) A, GTA ~func(R) T.Tuple1[A], R, A any](a GA) GTA { + return apply.SequenceT1( + Map[GA, GTA, R, A, T.Tuple1[A]], + + a, + ) +} + +func SequenceT2[GA ~func(R) A, GB ~func(R) B, GTAB ~func(R) T.Tuple2[A, B], R, A, B any](a GA, b GB) GTAB { + return apply.SequenceT2( + Map[GA, func(R) func(B) T.Tuple2[A, B], R, A, func(B) T.Tuple2[A, B]], + Ap[GB, GTAB, func(R) func(B) T.Tuple2[A, B], R, B, T.Tuple2[A, B]], + + a, b, + ) +} + +func SequenceT3[GA ~func(R) A, GB ~func(R) B, GC ~func(R) C, GTABC ~func(R) T.Tuple3[A, B, C], R, A, B, C any](a GA, b GB, c GC) GTABC { + return apply.SequenceT3( + Map[GA, func(R) func(B) func(C) T.Tuple3[A, B, C], R, A, func(B) func(C) T.Tuple3[A, B, C]], + Ap[GB, func(R) func(C) T.Tuple3[A, B, C], func(R) func(B) func(C) T.Tuple3[A, B, C], R, B, func(C) T.Tuple3[A, B, C]], + Ap[GC, GTABC, func(R) func(C) T.Tuple3[A, B, C], R, C, T.Tuple3[A, B, C]], + + a, b, c, + ) +} + +func SequenceT4[GA ~func(R) A, GB ~func(R) B, GC ~func(R) C, GD ~func(R) D, GTABCD ~func(R) T.Tuple4[A, B, C, D], R, A, B, C, D any](a GA, b GB, c GC, d GD) GTABCD { + return apply.SequenceT4( + Map[GA, func(R) func(B) func(C) func(D) T.Tuple4[A, B, C, D], R, A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], + Ap[GB, func(R) func(C) func(D) T.Tuple4[A, B, C, D], func(R) func(B) func(C) func(D) T.Tuple4[A, B, C, D], R, B, func(C) func(D) T.Tuple4[A, B, C, D]], + Ap[GC, func(R) func(D) T.Tuple4[A, B, C, D], func(R) func(C) func(D) T.Tuple4[A, B, C, D], R, C, func(D) T.Tuple4[A, B, C, D]], + Ap[GD, GTABCD, func(R) func(D) T.Tuple4[A, B, C, D], R, D, T.Tuple4[A, B, C, D]], + + a, b, c, d, + ) +} diff --git a/v2/reader/reader.go b/v2/reader/reader.go new file mode 100644 index 0000000..faeff73 --- /dev/null +++ b/v2/reader/reader.go @@ -0,0 +1,115 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + G "github.com/IBM/fp-go/v2/reader/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// The purpose of the `Reader` monad is to avoid threading arguments through multiple functions in order to only get them where they are needed. +// The first template argument `R` is the the context to read from, the second argument `A` is the return value of the monad +type Reader[R, A any] func(R) A + +// MakeReader creates a reader, i.e. a method that accepts a context and that returns a value +func MakeReader[R, A any](r Reader[R, A]) Reader[R, A] { + return G.MakeReader(r) +} + +// Ask reads the current context +func Ask[R any]() Reader[R, R] { + return G.Ask[Reader[R, R]]() +} + +// Asks projects a value from the global context in a Reader +func Asks[R, A any](f Reader[R, A]) Reader[R, A] { + return G.Asks(f) +} + +func AsksReader[R, A any](f func(R) Reader[R, A]) Reader[R, A] { + return G.AsksReader(f) +} + +func MonadMap[E, A, B any](fa Reader[E, A], f func(A) B) Reader[E, B] { + return G.MonadMap[Reader[E, A], Reader[E, B]](fa, f) +} + +// Map can be used to turn functions `func(A)B` into functions `(fa F[A])F[B]` whose argument and return types +// use the type constructor `F` to represent some computational context. +func Map[E, A, B any](f func(A) B) func(Reader[E, A]) Reader[E, B] { + return G.Map[Reader[E, A], Reader[E, B]](f) +} + +func MonadAp[B, R, A any](fab Reader[R, func(A) B], fa Reader[R, A]) Reader[R, B] { + return G.MonadAp[Reader[R, A], Reader[R, B]](fab, fa) +} + +// Ap applies a function to an argument under a type constructor. +func Ap[B, R, A any](fa Reader[R, A]) func(Reader[R, func(A) B]) Reader[R, B] { + return G.Ap[Reader[R, A], Reader[R, B], Reader[R, func(A) B]](fa) +} + +func Of[R, A any](a A) Reader[R, A] { + return G.Of[Reader[R, A]](a) +} + +func MonadChain[R, A, B any](ma Reader[R, A], f func(A) Reader[R, B]) Reader[R, B] { + return G.MonadChain(ma, f) +} + +// Chain composes computations in sequence, using the return value of one computation to determine the next computation. +func Chain[R, A, B any](f func(A) Reader[R, B]) func(Reader[R, A]) Reader[R, B] { + return G.Chain[Reader[R, A]](f) +} + +func Flatten[R, A any](mma func(R) Reader[R, A]) Reader[R, A] { + return G.Flatten(mma) +} + +func Compose[R, B, C any](ab Reader[R, B]) func(Reader[B, C]) Reader[R, C] { + return G.Compose[Reader[R, B], Reader[B, C], Reader[R, C]](ab) +} + +func First[A, B, C any](pab Reader[A, B]) Reader[T.Tuple2[A, C], T.Tuple2[B, C]] { + return G.First[Reader[A, B], Reader[T.Tuple2[A, C], T.Tuple2[B, C]]](pab) +} + +func Second[A, B, C any](pbc Reader[B, C]) Reader[T.Tuple2[A, B], T.Tuple2[A, C]] { + return G.Second[Reader[B, C], Reader[T.Tuple2[A, B], T.Tuple2[A, C]]](pbc) +} + +func Promap[E, A, D, B any](f func(D) E, g func(A) B) func(Reader[E, A]) Reader[D, B] { + return G.Promap[Reader[E, A], Reader[D, B]](f, g) +} + +// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s +// `contramap`). +func Local[R2, R1, A any](f func(R2) R1) func(Reader[R1, A]) Reader[R2, A] { + return G.Local[Reader[R1, A], Reader[R2, A]](f) +} + +// Read applies a context to a reader to obtain its value +func Read[E, A any](e E) func(Reader[E, A]) A { + return G.Read[Reader[E, A]](e) +} + +func MonadFlap[R, A, B any](fab Reader[R, func(A) B], a A) Reader[R, B] { + return G.MonadFlap[Reader[R, func(A) B], Reader[R, B]](fab, a) +} + +func Flap[R, A, B any](a A) func(Reader[R, func(A) B]) Reader[R, B] { + return G.Flap[Reader[R, func(A) B], Reader[R, B]](a) +} diff --git a/v2/reader/reader_test.go b/v2/reader/reader_test.go new file mode 100644 index 0000000..ca2cd8b --- /dev/null +++ b/v2/reader/reader_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" + + "github.com/IBM/fp-go/v2/internal/utils" +) + +func TestMap(t *testing.T) { + + assert.Equal(t, 2, F.Pipe1(Of[string](1), Map[string](utils.Double))("")) +} + +func TestAp(t *testing.T) { + assert.Equal(t, 2, F.Pipe1(Of[int](utils.Double), Ap[int, int, int](Of[int](1)))(0)) +} diff --git a/v2/reader/semigroup.go b/v2/reader/semigroup.go new file mode 100644 index 0000000..6f89954 --- /dev/null +++ b/v2/reader/semigroup.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func ApplySemigroup[R, A any]( + _map func(func(R) A, func(A) func(A) A) func(R, func(A) A), + _ap func(func(R, func(A) A), func(R) A) func(R) A, + + s S.Semigroup[A], +) S.Semigroup[func(R) A] { + return S.ApplySemigroup(_map, _ap, s) +} + +func ApplicativeMonoid[R, A any]( + _of func(A) func(R) A, + _map func(func(R) A, func(A) func(A) A) func(R, func(A) A), + _ap func(func(R, func(A) A), func(R) A) func(R) A, + + m M.Monoid[A], +) M.Monoid[func(R) A] { + return M.ApplicativeMonoid(_of, _map, _ap, m) +} diff --git a/v2/reader/sequence.go b/v2/reader/sequence.go new file mode 100644 index 0000000..2461896 --- /dev/null +++ b/v2/reader/sequence.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reader + +import ( + G "github.com/IBM/fp-go/v2/reader/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[R, A any](a Reader[R, A]) Reader[R, T.Tuple1[A]] { + return G.SequenceT1[Reader[R, A], Reader[R, T.Tuple1[A]]](a) +} + +func SequenceT2[R, A, B any](a Reader[R, A], b Reader[R, B]) Reader[R, T.Tuple2[A, B]] { + return G.SequenceT2[Reader[R, A], Reader[R, B], Reader[R, T.Tuple2[A, B]]](a, b) +} + +func SequenceT3[R, A, B, C any](a Reader[R, A], b Reader[R, B], c Reader[R, C]) Reader[R, T.Tuple3[A, B, C]] { + return G.SequenceT3[Reader[R, A], Reader[R, B], Reader[R, C], Reader[R, T.Tuple3[A, B, C]]](a, b, c) +} + +func SequenceT4[R, A, B, C, D any](a Reader[R, A], b Reader[R, B], c Reader[R, C], d Reader[R, D]) Reader[R, T.Tuple4[A, B, C, D]] { + return G.SequenceT4[Reader[R, A], Reader[R, B], Reader[R, C], Reader[R, D], Reader[R, T.Tuple4[A, B, C, D]]](a, b, c, d) +} diff --git a/v2/readereither/array.go b/v2/readereither/array.go new file mode 100644 index 0000000..adcb1a2 --- /dev/null +++ b/v2/readereither/array.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + G "github.com/IBM/fp-go/v2/readereither/generic" +) + +// TraverseArray transforms an array +func TraverseArray[E, L, A, B any](f func(A) ReaderEither[E, L, B]) func([]A) ReaderEither[E, L, []B] { + return G.TraverseArray[ReaderEither[E, L, B], ReaderEither[E, L, []B], []A](f) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[E, L, A, B any](f func(int, A) ReaderEither[E, L, B]) func([]A) ReaderEither[E, L, []B] { + return G.TraverseArrayWithIndex[ReaderEither[E, L, B], ReaderEither[E, L, []B], []A](f) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[E, L, A any](ma []ReaderEither[E, L, A]) ReaderEither[E, L, []A] { + return G.SequenceArray[ReaderEither[E, L, A], ReaderEither[E, L, []A]](ma) +} diff --git a/v2/readereither/array_test.go b/v2/readereither/array_test.go new file mode 100644 index 0000000..b842197 --- /dev/null +++ b/v2/readereither/array_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "context" + "testing" + + A "github.com/IBM/fp-go/v2/array" + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestSequenceArray(t *testing.T) { + + n := 10 + + readers := A.MakeBy(n, Of[context.Context, error, int]) + exp := ET.Of[error](A.MakeBy(n, F.Identity[int])) + + g := F.Pipe1( + readers, + SequenceArray[context.Context, error, int], + ) + + assert.Equal(t, exp, g(context.Background())) +} diff --git a/v2/readereither/bind.go b/v2/readereither/bind.go new file mode 100644 index 0000000..8bd50d5 --- /dev/null +++ b/v2/readereither/bind.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + G "github.com/IBM/fp-go/v2/readereither/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[R, E, S any]( + empty S, +) ReaderEither[R, E, S] { + return G.Do[ReaderEither[R, E, S], R, E, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) ReaderEither[R, E, T], +) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] { + return G.Bind[ReaderEither[R, E, S1], ReaderEither[R, E, S2], ReaderEither[R, E, T], R, E, S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] { + return G.Let[ReaderEither[R, E, S1], ReaderEither[R, E, S2], R, E, S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] { + return G.LetTo[ReaderEither[R, E, S1], ReaderEither[R, E, S2], R, E, S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[R, E, S1, T any]( + setter func(T) S1, +) func(ReaderEither[R, E, T]) ReaderEither[R, E, S1] { + return G.BindTo[ReaderEither[R, E, S1], ReaderEither[R, E, T], R, E, S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa ReaderEither[R, E, T], +) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] { + return G.ApS[ReaderEither[R, E, S1], ReaderEither[R, E, S2], ReaderEither[R, E, T], R, E, S1, S2, T](setter, fa) +} diff --git a/v2/readereither/bind_test.go b/v2/readereither/bind_test.go new file mode 100644 index 0000000..b9c491d --- /dev/null +++ b/v2/readereither/bind_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "context" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) ReaderEither[context.Context, error, string] { + return Of[context.Context, error]("Doe") +} + +func getGivenName(s utils.WithLastName) ReaderEither[context.Context, error, string] { + return Of[context.Context, error]("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do[context.Context, error](utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map[context.Context, error](utils.GetFullName), + ) + + assert.Equal(t, res(context.Background()), E.Of[error]("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do[context.Context, error](utils.Empty), + ApS(utils.SetLastName, Of[context.Context, error]("Doe")), + ApS(utils.SetGivenName, Of[context.Context, error]("John")), + Map[context.Context, error](utils.GetFullName), + ) + + assert.Equal(t, res(context.Background()), E.Of[error]("John Doe")) +} diff --git a/v2/readereither/curry.go b/v2/readereither/curry.go new file mode 100644 index 0000000..ec65530 --- /dev/null +++ b/v2/readereither/curry.go @@ -0,0 +1,51 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + G "github.com/IBM/fp-go/v2/readereither/generic" +) + +// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func Curry0[R, A any](f func(R) (A, error)) ReaderEither[R, error, A] { + return G.Curry0[ReaderEither[R, error, A]](f) +} + +func Curry1[R, T1, A any](f func(R, T1) (A, error)) func(T1) ReaderEither[R, error, A] { + return G.Curry1[ReaderEither[R, error, A]](f) +} + +func Curry2[R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1) func(T2) ReaderEither[R, error, A] { + return G.Curry2[ReaderEither[R, error, A]](f) +} + +func Curry3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) ReaderEither[R, error, A] { + return G.Curry3[ReaderEither[R, error, A]](f) +} + +func Uncurry1[R, T1, A any](f func(T1) ReaderEither[R, error, A]) func(R, T1) (A, error) { + return G.Uncurry1(f) +} + +func Uncurry2[R, T1, T2, A any](f func(T1) func(T2) ReaderEither[R, error, A]) func(R, T1, T2) (A, error) { + return G.Uncurry2(f) +} + +func Uncurry3[R, T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderEither[R, error, A]) func(R, T1, T2, T3) (A, error) { + return G.Uncurry3(f) +} diff --git a/v2/readereither/from.go b/v2/readereither/from.go new file mode 100644 index 0000000..d306808 --- /dev/null +++ b/v2/readereither/from.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + G "github.com/IBM/fp-go/v2/readereither/generic" +) + +// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func From0[R, A any](f func(R) (A, error)) func() ReaderEither[R, error, A] { + return G.From0[ReaderEither[R, error, A]](f) +} + +func From1[R, T1, A any](f func(R, T1) (A, error)) func(T1) ReaderEither[R, error, A] { + return G.From1[ReaderEither[R, error, A]](f) +} + +func From2[R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1, T2) ReaderEither[R, error, A] { + return G.From2[ReaderEither[R, error, A]](f) +} + +func From3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderEither[R, error, A] { + return G.From3[ReaderEither[R, error, A]](f) +} diff --git a/v2/readereither/generic/array.go b/v2/readereither/generic/array.go new file mode 100644 index 0000000..52a3cf5 --- /dev/null +++ b/v2/readereither/generic/array.go @@ -0,0 +1,60 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" +) + +// MonadTraverseArray transforms an array +func MonadTraverseArray[GB ~func(E) ET.Either[L, B], GBS ~func(E) ET.Either[L, BBS], AAS ~[]A, BBS ~[]B, L, E, A, B any](ma AAS, f func(A) GB) GBS { + return RA.MonadTraverse[AAS]( + Of[GBS, L, E, BBS], + Map[GBS, func(E) ET.Either[L, func(B) BBS], L, E, BBS, func(B) BBS], + Ap[GB, GBS, func(E) ET.Either[L, func(B) BBS], L, E, B, BBS], + + ma, f, + ) +} + +// TraverseArray transforms an array +func TraverseArray[GB ~func(E) ET.Either[L, B], GBS ~func(E) ET.Either[L, BBS], AAS ~[]A, BBS ~[]B, L, E, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, L, E, BBS], + Map[GBS, func(E) ET.Either[L, func(B) BBS], L, E, BBS, func(B) BBS], + Ap[GB, GBS, func(E) ET.Either[L, func(B) BBS], L, E, B, BBS], + + f, + ) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[GB ~func(E) ET.Either[L, B], GBS ~func(E) ET.Either[L, BBS], AAS ~[]A, BBS ~[]B, L, E, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, L, E, BBS], + Map[GBS, func(E) ET.Either[L, func(B) BBS], L, E, BBS, func(B) BBS], + Ap[GB, GBS, func(E) ET.Either[L, func(B) BBS], L, E, B, BBS], + + f, + ) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[GA ~func(E) ET.Either[L, A], GAS ~func(E) ET.Either[L, AAS], AAS ~[]A, GAAS ~[]GA, L, E, A any](ma GAAS) GAS { + return MonadTraverseArray[GA, GAS](ma, F.Identity[GA]) +} diff --git a/v2/readereither/generic/bind.go b/v2/readereither/generic/bind.go new file mode 100644 index 0000000..2c03561 --- /dev/null +++ b/v2/readereither/generic/bind.go @@ -0,0 +1,90 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GS ~func(R) ET.Either[E, S], R, E, S any]( + empty S, +) GS { + return Of[GS, E, R, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], GT ~func(R) ET.Either[E, T], R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GT, +) func(GS1) GS2 { + return C.Bind( + Chain[GS1, GS2, E, R, S1, S2], + Map[GT, GS2, E, R, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], R, E, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GS1) GS2 { + return F.Let( + Map[GS1, GS2, E, R, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], R, E, S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GS1) GS2 { + return F.LetTo( + Map[GS1, GS2, E, R, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GS1 ~func(R) ET.Either[E, S1], GT ~func(R) ET.Either[E, T], R, E, S1, T any]( + setter func(T) S1, +) func(GT) GS1 { + return C.BindTo( + Map[GT, GS1, E, R, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], GT ~func(R) ET.Either[E, T], R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa GT, +) func(GS1) GS2 { + return A.ApS( + Ap[GT, GS2, func(R) ET.Either[E, func(T) S2], E, R, T, S2], + Map[GS1, func(R) ET.Either[E, func(T) S2], E, R, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/readereither/generic/curry.go b/v2/readereither/generic/curry.go new file mode 100644 index 0000000..a801e54 --- /dev/null +++ b/v2/readereither/generic/curry.go @@ -0,0 +1,52 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/reader/generic" +) + +// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func Curry0[GEA ~func(R) ET.Either[error, A], R, A any](f func(R) (A, error)) GEA { + return G.Curry0[GEA](ET.Eitherize1(f)) +} + +func Curry1[GEA ~func(R) ET.Either[error, A], R, T1, A any](f func(R, T1) (A, error)) func(T1) GEA { + return G.Curry1[GEA](ET.Eitherize2(f)) +} + +func Curry2[GEA ~func(R) ET.Either[error, A], R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1) func(T2) GEA { + return G.Curry2[GEA](ET.Eitherize3(f)) +} + +func Curry3[GEA ~func(R) ET.Either[error, A], R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) GEA { + return G.Curry3[GEA](ET.Eitherize4(f)) +} + +func Uncurry1[GEA ~func(R) ET.Either[error, A], R, T1, A any](f func(T1) GEA) func(R, T1) (A, error) { + return ET.Uneitherize2(G.Uncurry1(f)) +} + +func Uncurry2[GEA ~func(R) ET.Either[error, A], R, T1, T2, A any](f func(T1) func(T2) GEA) func(R, T1, T2) (A, error) { + return ET.Uneitherize3(G.Uncurry2(f)) +} + +func Uncurry3[GEA ~func(R) ET.Either[error, A], R, T1, T2, T3, A any](f func(T1) func(T2) func(T3) GEA) func(R, T1, T2, T3) (A, error) { + return ET.Uneitherize4(G.Uncurry3(f)) +} diff --git a/v2/readereither/generic/from.go b/v2/readereither/generic/from.go new file mode 100644 index 0000000..cd055f9 --- /dev/null +++ b/v2/readereither/generic/from.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/reader/generic" +) + +// these functions From a golang function with the context as the firsr parameter into a either reader with the context as the last parameter +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func From0[GEA ~func(R) ET.Either[error, A], R, A any](f func(R) (A, error)) func() GEA { + return G.From0[GEA](ET.Eitherize1(f)) +} + +func From1[GEA ~func(R) ET.Either[error, A], R, T1, A any](f func(R, T1) (A, error)) func(T1) GEA { + return G.From1[GEA](ET.Eitherize2(f)) +} + +func From2[GEA ~func(R) ET.Either[error, A], R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1, T2) GEA { + return G.From2[GEA](ET.Eitherize3(f)) +} + +func From3[GEA ~func(R) ET.Either[error, A], R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1, T2, T3) GEA { + return G.From3[GEA](ET.Eitherize4(f)) +} diff --git a/v2/readereither/generic/reader.go b/v2/readereither/generic/reader.go new file mode 100644 index 0000000..545e571 --- /dev/null +++ b/v2/readereither/generic/reader.go @@ -0,0 +1,175 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/eithert" + FE "github.com/IBM/fp-go/v2/internal/fromeither" + FR "github.com/IBM/fp-go/v2/internal/fromreader" + FC "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/readert" + O "github.com/IBM/fp-go/v2/option" + R "github.com/IBM/fp-go/v2/reader/generic" +) + +func MakeReaderEither[GEA ~func(E) ET.Either[L, A], L, E, A any](f func(E) ET.Either[L, A]) GEA { + return f +} + +func FromEither[GEA ~func(E) ET.Either[L, A], L, E, A any](e ET.Either[L, A]) GEA { + return R.Of[GEA](e) +} + +func RightReader[GA ~func(E) A, GEA ~func(E) ET.Either[L, A], L, E, A any](r GA) GEA { + return eithert.RightF(R.MonadMap[GA, GEA, E, A, ET.Either[L, A]], r) +} + +func LeftReader[GL ~func(E) L, GEA ~func(E) ET.Either[L, A], L, E, A any](l GL) GEA { + return eithert.LeftF(R.MonadMap[GL, GEA, E, L, ET.Either[L, A]], l) +} + +func Left[GEA ~func(E) ET.Either[L, A], L, E, A any](l L) GEA { + return eithert.Left(R.Of[GEA, E, ET.Either[L, A]], l) +} + +func Right[GEA ~func(E) ET.Either[L, A], L, E, A any](r A) GEA { + return eithert.Right(R.Of[GEA, E, ET.Either[L, A]], r) +} + +func FromReader[GA ~func(E) A, GEA ~func(E) ET.Either[L, A], L, E, A any](r GA) GEA { + return RightReader[GA, GEA](r) +} + +func MonadMap[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](fa GEA, f func(A) B) GEB { + return readert.MonadMap[GEA, GEB](ET.MonadMap[L, A, B], fa, f) +} + +func Map[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](f func(A) B) func(GEA) GEB { + return readert.Map[GEA, GEB](ET.Map[L, A, B], f) +} + +func MonadChain[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](ma GEA, f func(A) GEB) GEB { + return readert.MonadChain(ET.MonadChain[L, A, B], ma, f) +} + +func Chain[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](f func(A) GEB) func(GEA) GEB { + return F.Bind2nd(MonadChain[GEA, GEB, L, E, A, B], f) +} + +func Of[GEA ~func(E) ET.Either[L, A], L, E, A any](a A) GEA { + return readert.MonadOf[GEA](ET.Of[L, A], a) +} + +func MonadAp[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], GEFAB ~func(E) ET.Either[L, func(A) B], L, E, A, B any](fab GEFAB, fa GEA) GEB { + return readert.MonadAp[GEA, GEB, GEFAB, E, A](ET.MonadAp[B, L, A], fab, fa) +} + +func Ap[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], GEFAB ~func(E) ET.Either[L, func(A) B], L, E, A, B any](fa GEA) func(GEFAB) GEB { + return F.Bind2nd(MonadAp[GEA, GEB, GEFAB, L, E, A, B], fa) +} + +func FromPredicate[GEA ~func(E) ET.Either[L, A], L, E, A any](pred func(A) bool, onFalse func(A) L) func(A) GEA { + return FE.FromPredicate(FromEither[GEA, L, E, A], pred, onFalse) +} + +func Fold[GEA ~func(E) ET.Either[L, A], GB ~func(E) B, E, L, A, B any](onLeft func(L) GB, onRight func(A) GB) func(GEA) GB { + return eithert.MatchE(R.MonadChain[GEA, GB, E, ET.Either[L, A], B], onLeft, onRight) +} + +func GetOrElse[GEA ~func(E) ET.Either[L, A], GA ~func(E) A, E, L, A any](onLeft func(L) GA) func(GEA) GA { + return eithert.GetOrElse(R.MonadChain[GEA, GA, E, ET.Either[L, A], A], R.Of[GA, E, A], onLeft) +} + +func OrElse[GEA1 ~func(E) ET.Either[L1, A], GEA2 ~func(E) ET.Either[L2, A], E, L1, A, L2 any](onLeft func(L1) GEA2) func(GEA1) GEA2 { + return eithert.OrElse(R.MonadChain[GEA1, GEA2, E, ET.Either[L1, A], ET.Either[L2, A]], R.Of[GEA2, E, ET.Either[L2, A]], onLeft) +} + +func OrLeft[GEA1 ~func(E) ET.Either[L1, A], GEA2 ~func(E) ET.Either[L2, A], GE2 ~func(E) L2, L1, E, L2, A any](onLeft func(L1) GE2) func(GEA1) GEA2 { + return eithert.OrLeft( + R.MonadChain[GEA1, GEA2, E, ET.Either[L1, A], ET.Either[L2, A]], + R.MonadMap[GE2, GEA2, E, L2, ET.Either[L2, A]], + R.Of[GEA2, E, ET.Either[L2, A]], + onLeft, + ) +} + +func Ask[GEE ~func(E) ET.Either[L, E], E, L any]() GEE { + return FR.Ask(FromReader[func(E) E, GEE, L, E, E])() +} + +func Asks[GA ~func(E) A, GEA ~func(E) ET.Either[L, A], E, L, A any](r GA) GEA { + return FR.Asks(FromReader[GA, GEA, L, E, A])(r) +} + +func MonadChainEitherK[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](ma GEA, f func(A) ET.Either[L, B]) GEB { + return FE.MonadChainEitherK( + MonadChain[GEA, GEB, L, E, A, B], + FromEither[GEB, L, E, B], + ma, + f, + ) +} + +func ChainEitherK[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](f func(A) ET.Either[L, B]) func(ma GEA) GEB { + return F.Bind2nd(MonadChainEitherK[GEA, GEB, L, E, A, B], f) +} + +func ChainOptionK[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A, B any](onNone func() L) func(func(A) O.Option[B]) func(GEA) GEB { + return FE.ChainOptionK(MonadChain[GEA, GEB, L, E, A, B], FromEither[GEB, L, E, B], onNone) +} + +func Flatten[GEA ~func(E) ET.Either[L, A], GGA ~func(E) ET.Either[L, GEA], L, E, A any](mma GGA) GEA { + return MonadChain(mma, F.Identity[GEA]) +} + +func MonadBiMap[GA ~func(E) ET.Either[E1, A], GB ~func(E) ET.Either[E2, B], E, E1, E2, A, B any](fa GA, f func(E1) E2, g func(A) B) GB { + return eithert.MonadBiMap(R.MonadMap[GA, GB, E, ET.Either[E1, A], ET.Either[E2, B]], fa, f, g) +} + +// BiMap maps a pair of functions over the two type arguments of the bifunctor. +func BiMap[GA ~func(E) ET.Either[E1, A], GB ~func(E) ET.Either[E2, B], E, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(GA) GB { + return eithert.BiMap(R.Map[GA, GB, E, ET.Either[E1, A], ET.Either[E2, B]], f, g) +} + +// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s +// `contramap`). +func Local[GA1 ~func(R1) ET.Either[E, A], GA2 ~func(R2) ET.Either[E, A], R2, R1, E, A any](f func(R2) R1) func(GA1) GA2 { + return R.Local[GA1, GA2](f) +} + +// Read applies a context to a reader to obtain its value +func Read[GA ~func(E) ET.Either[E1, A], E, E1, A any](e E) func(GA) ET.Either[E1, A] { + return R.Read[GA](e) +} + +func MonadFlap[GEFAB ~func(E) ET.Either[L, func(A) B], GEB ~func(E) ET.Either[L, B], L, E, A, B any](fab GEFAB, a A) GEB { + return FC.MonadFlap(MonadMap[GEFAB, GEB], fab, a) +} + +func Flap[GEFAB ~func(E) ET.Either[L, func(A) B], GEB ~func(E) ET.Either[L, B], L, E, A, B any](a A) func(GEFAB) GEB { + return FC.Flap(Map[GEFAB, GEB], a) +} + +func MonadMapLeft[GA1 ~func(C) ET.Either[E1, A], GA2 ~func(C) ET.Either[E2, A], C, E1, E2, A any](fa GA1, f func(E1) E2) GA2 { + return eithert.MonadMapLeft(R.MonadMap[GA1, GA2, C, ET.Either[E1, A], ET.Either[E2, A]], fa, f) +} + +// MapLeft applies a mapping function to the error channel +func MapLeft[GA1 ~func(C) ET.Either[E1, A], GA2 ~func(C) ET.Either[E2, A], C, E1, E2, A any](f func(E1) E2) func(GA1) GA2 { + return F.Bind2nd(MonadMapLeft[GA1, GA2, C, E1, E2, A], f) +} diff --git a/v2/readereither/generic/sequence.go b/v2/readereither/generic/sequence.go new file mode 100644 index 0000000..3fc1681 --- /dev/null +++ b/v2/readereither/generic/sequence.go @@ -0,0 +1,80 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[ + GA ~func(E) ET.Either[L, A], + GTA ~func(E) ET.Either[L, T.Tuple1[A]], + L, E, A any](a GA) GTA { + return apply.SequenceT1( + Map[GA, GTA, L, E, A, T.Tuple1[A]], + + a, + ) +} + +func SequenceT2[ + GA ~func(E) ET.Either[L, A], + GB ~func(E) ET.Either[L, B], + GTAB ~func(E) ET.Either[L, T.Tuple2[A, B]], + L, E, A, B any](a GA, b GB) GTAB { + return apply.SequenceT2( + Map[GA, func(E) ET.Either[L, func(B) T.Tuple2[A, B]], L, E, A, func(B) T.Tuple2[A, B]], + Ap[GB, GTAB, func(E) ET.Either[L, func(B) T.Tuple2[A, B]], L, E, B, T.Tuple2[A, B]], + + a, b, + ) +} + +func SequenceT3[ + GA ~func(E) ET.Either[L, A], + GB ~func(E) ET.Either[L, B], + GC ~func(E) ET.Either[L, C], + GTABC ~func(E) ET.Either[L, T.Tuple3[A, B, C]], + L, E, A, B, C any](a GA, b GB, c GC) GTABC { + return apply.SequenceT3( + Map[GA, func(E) ET.Either[L, func(B) func(C) T.Tuple3[A, B, C]], L, E, A, func(B) func(C) T.Tuple3[A, B, C]], + Ap[GB, func(E) ET.Either[L, func(C) T.Tuple3[A, B, C]], func(E) ET.Either[L, func(B) func(C) T.Tuple3[A, B, C]], L, E, B, func(C) T.Tuple3[A, B, C]], + Ap[GC, GTABC, func(E) ET.Either[L, func(C) T.Tuple3[A, B, C]], L, E, C, T.Tuple3[A, B, C]], + + a, b, c, + ) +} + +func SequenceT4[ + GA ~func(E) ET.Either[L, A], + GB ~func(E) ET.Either[L, B], + GC ~func(E) ET.Either[L, C], + GD ~func(E) ET.Either[L, D], + GTABCD ~func(E) ET.Either[L, T.Tuple4[A, B, C, D]], + L, E, A, B, C, D any](a GA, b GB, c GC, d GD) GTABCD { + return apply.SequenceT4( + Map[GA, func(E) ET.Either[L, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], L, E, A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], + Ap[GB, func(E) ET.Either[L, func(C) func(D) T.Tuple4[A, B, C, D]], func(E) ET.Either[L, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], L, E, B, func(C) func(D) T.Tuple4[A, B, C, D]], + Ap[GC, func(E) ET.Either[L, func(D) T.Tuple4[A, B, C, D]], func(E) ET.Either[L, func(C) func(D) T.Tuple4[A, B, C, D]], L, E, C, func(D) T.Tuple4[A, B, C, D]], + Ap[GD, GTABCD, func(E) ET.Either[L, func(D) T.Tuple4[A, B, C, D]], L, E, D, T.Tuple4[A, B, C, D]], + + a, b, c, d, + ) +} diff --git a/v2/readereither/reader.go b/v2/readereither/reader.go new file mode 100644 index 0000000..db75165 --- /dev/null +++ b/v2/readereither/reader.go @@ -0,0 +1,162 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + ET "github.com/IBM/fp-go/v2/either" + O "github.com/IBM/fp-go/v2/option" + R "github.com/IBM/fp-go/v2/reader" + G "github.com/IBM/fp-go/v2/readereither/generic" +) + +type ReaderEither[E, L, A any] R.Reader[E, ET.Either[L, A]] + +func MakeReaderEither[L, E, A any](f func(E) ET.Either[L, A]) ReaderEither[E, L, A] { + return G.MakeReaderEither[ReaderEither[E, L, A]](f) +} + +func FromEither[E, L, A any](e ET.Either[L, A]) ReaderEither[E, L, A] { + return G.FromEither[ReaderEither[E, L, A]](e) +} + +func RightReader[L, E, A any](r R.Reader[E, A]) ReaderEither[E, L, A] { + return G.RightReader[R.Reader[E, A], ReaderEither[E, L, A]](r) +} + +func LeftReader[A, E, L any](l R.Reader[E, L]) ReaderEither[E, L, A] { + return G.LeftReader[R.Reader[E, L], ReaderEither[E, L, A]](l) +} + +func Left[E, A, L any](l L) ReaderEither[E, L, A] { + return G.Left[ReaderEither[E, L, A]](l) +} + +func Right[E, L, A any](r A) ReaderEither[E, L, A] { + return G.Right[ReaderEither[E, L, A]](r) +} + +func FromReader[E, L, A any](r R.Reader[E, A]) ReaderEither[E, L, A] { + return G.FromReader[R.Reader[E, A], ReaderEither[E, L, A]](r) +} + +func MonadMap[E, L, A, B any](fa ReaderEither[E, L, A], f func(A) B) ReaderEither[E, L, B] { + return G.MonadMap[ReaderEither[E, L, A], ReaderEither[E, L, B]](fa, f) +} + +func Map[E, L, A, B any](f func(A) B) func(ReaderEither[E, L, A]) ReaderEither[E, L, B] { + return G.Map[ReaderEither[E, L, A], ReaderEither[E, L, B]](f) +} + +func MonadChain[E, L, A, B any](ma ReaderEither[E, L, A], f func(A) ReaderEither[E, L, B]) ReaderEither[E, L, B] { + return G.MonadChain[ReaderEither[E, L, A], ReaderEither[E, L, B]](ma, f) +} + +func Chain[E, L, A, B any](f func(A) ReaderEither[E, L, B]) func(ReaderEither[E, L, A]) ReaderEither[E, L, B] { + return G.Chain[ReaderEither[E, L, A], ReaderEither[E, L, B]](f) +} + +func Of[E, L, A any](a A) ReaderEither[E, L, A] { + return G.Of[ReaderEither[E, L, A]](a) +} + +func MonadAp[E, L, A, B any](fab ReaderEither[E, L, func(A) B], fa ReaderEither[E, L, A]) ReaderEither[E, L, B] { + return G.MonadAp[ReaderEither[E, L, A], ReaderEither[E, L, B], ReaderEither[E, L, func(A) B]](fab, fa) +} + +func Ap[B, E, L, A any](fa ReaderEither[E, L, A]) func(ReaderEither[E, L, func(A) B]) ReaderEither[E, L, B] { + return G.Ap[ReaderEither[E, L, A], ReaderEither[E, L, B], ReaderEither[E, L, func(A) B]](fa) +} + +func FromPredicate[E, L, A any](pred func(A) bool, onFalse func(A) L) func(A) ReaderEither[E, L, A] { + return G.FromPredicate[ReaderEither[E, L, A]](pred, onFalse) +} + +func Fold[E, L, A, B any](onLeft func(L) R.Reader[E, B], onRight func(A) R.Reader[E, B]) func(ReaderEither[E, L, A]) R.Reader[E, B] { + return G.Fold[ReaderEither[E, L, A]](onLeft, onRight) +} + +func GetOrElse[E, L, A any](onLeft func(L) R.Reader[E, A]) func(ReaderEither[E, L, A]) R.Reader[E, A] { + return G.GetOrElse[ReaderEither[E, L, A]](onLeft) +} + +func OrElse[E, L1, A, L2 any](onLeft func(L1) ReaderEither[E, L2, A]) func(ReaderEither[E, L1, A]) ReaderEither[E, L2, A] { + return G.OrElse[ReaderEither[E, L1, A]](onLeft) +} + +func OrLeft[A, L1, E, L2 any](onLeft func(L1) R.Reader[E, L2]) func(ReaderEither[E, L1, A]) ReaderEither[E, L2, A] { + return G.OrLeft[ReaderEither[E, L1, A], ReaderEither[E, L2, A]](onLeft) +} + +func Ask[E, L any]() ReaderEither[E, L, E] { + return G.Ask[ReaderEither[E, L, E]]() +} + +func Asks[L, E, A any](r R.Reader[E, A]) ReaderEither[E, L, A] { + return G.Asks[R.Reader[E, A], ReaderEither[E, L, A]](r) +} + +func MonadChainEitherK[A, B, L, E any](ma ReaderEither[E, L, A], f func(A) ET.Either[L, B]) ReaderEither[E, L, B] { + return G.MonadChainEitherK[ReaderEither[E, L, A], ReaderEither[E, L, B]](ma, f) +} + +func ChainEitherK[A, B, L, E any](f func(A) ET.Either[L, B]) func(ma ReaderEither[E, L, A]) ReaderEither[E, L, B] { + return G.ChainEitherK[ReaderEither[E, L, A], ReaderEither[E, L, B]](f) +} + +func ChainOptionK[E, A, B, L any](onNone func() L) func(func(A) O.Option[B]) func(ReaderEither[E, L, A]) ReaderEither[E, L, B] { + return G.ChainOptionK[ReaderEither[E, L, A], ReaderEither[E, L, B]](onNone) +} + +func Flatten[E, L, A any](mma ReaderEither[E, L, ReaderEither[E, L, A]]) ReaderEither[E, L, A] { + return G.Flatten(mma) +} + +func MonadBiMap[E, E1, E2, A, B any](fa ReaderEither[E, E1, A], f func(E1) E2, g func(A) B) ReaderEither[E, E2, B] { + return G.MonadBiMap[ReaderEither[E, E1, A], ReaderEither[E, E2, B]](fa, f, g) +} + +// BiMap maps a pair of functions over the two type arguments of the bifunctor. +func BiMap[E, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderEither[E, E1, A]) ReaderEither[E, E2, B] { + return G.BiMap[ReaderEither[E, E1, A], ReaderEither[E, E2, B]](f, g) +} + +// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s +// `contramap`). +func Local[E, A, R2, R1 any](f func(R2) R1) func(ReaderEither[R1, E, A]) ReaderEither[R2, E, A] { + return G.Local[ReaderEither[R1, E, A], ReaderEither[R2, E, A]](f) +} + +// Read applies a context to a reader to obtain its value +func Read[E1, A, E any](e E) func(ReaderEither[E, E1, A]) ET.Either[E1, A] { + return G.Read[ReaderEither[E, E1, A]](e) +} + +func MonadFlap[L, E, A, B any](fab ReaderEither[L, E, func(A) B], a A) ReaderEither[L, E, B] { + return G.MonadFlap[ReaderEither[L, E, func(A) B], ReaderEither[L, E, B]](fab, a) +} + +func Flap[L, E, B, A any](a A) func(ReaderEither[L, E, func(A) B]) ReaderEither[L, E, B] { + return G.Flap[ReaderEither[L, E, func(A) B], ReaderEither[L, E, B]](a) +} + +func MonadMapLeft[C, E1, E2, A any](fa ReaderEither[C, E1, A], f func(E1) E2) ReaderEither[C, E2, A] { + return G.MonadMapLeft[ReaderEither[C, E1, A], ReaderEither[C, E2, A]](fa, f) +} + +// MapLeft applies a mapping function to the error channel +func MapLeft[C, E1, E2, A any](f func(E1) E2) func(ReaderEither[C, E1, A]) ReaderEither[C, E2, A] { + return G.MapLeft[ReaderEither[C, E1, A], ReaderEither[C, E2, A]](f) +} diff --git a/v2/readereither/reader_test.go b/v2/readereither/reader_test.go new file mode 100644 index 0000000..4a1197b --- /dev/null +++ b/v2/readereither/reader_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "testing" + + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +type MyContext string + +const defaultContext MyContext = "default" + +func TestMap(t *testing.T) { + + g := F.Pipe1( + Of[MyContext, error](1), + Map[MyContext, error](utils.Double), + ) + + assert.Equal(t, ET.Of[error](2), g(defaultContext)) + +} + +func TestAp(t *testing.T) { + g := F.Pipe1( + Of[MyContext, error](utils.Double), + Ap[int](Of[MyContext, error](1)), + ) + assert.Equal(t, ET.Of[error](2), g(defaultContext)) + +} + +func TestFlatten(t *testing.T) { + + g := F.Pipe1( + Of[MyContext, string](Of[MyContext, string]("a")), + Flatten[MyContext, string, string], + ) + + assert.Equal(t, ET.Of[string]("a"), g(defaultContext)) +} diff --git a/v2/readereither/sequence.go b/v2/readereither/sequence.go new file mode 100644 index 0000000..9631309 --- /dev/null +++ b/v2/readereither/sequence.go @@ -0,0 +1,69 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + G "github.com/IBM/fp-go/v2/readereither/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[L, E, A any](a ReaderEither[E, L, A]) ReaderEither[E, L, T.Tuple1[A]] { + return G.SequenceT1[ + ReaderEither[E, L, A], + ReaderEither[E, L, T.Tuple1[A]], + ](a) +} + +func SequenceT2[L, E, A, B any]( + a ReaderEither[E, L, A], + b ReaderEither[E, L, B], +) ReaderEither[E, L, T.Tuple2[A, B]] { + return G.SequenceT2[ + ReaderEither[E, L, A], + ReaderEither[E, L, B], + ReaderEither[E, L, T.Tuple2[A, B]], + ](a, b) +} + +func SequenceT3[L, E, A, B, C any]( + a ReaderEither[E, L, A], + b ReaderEither[E, L, B], + c ReaderEither[E, L, C], +) ReaderEither[E, L, T.Tuple3[A, B, C]] { + return G.SequenceT3[ + ReaderEither[E, L, A], + ReaderEither[E, L, B], + ReaderEither[E, L, C], + ReaderEither[E, L, T.Tuple3[A, B, C]], + ](a, b, c) +} + +func SequenceT4[L, E, A, B, C, D any]( + a ReaderEither[E, L, A], + b ReaderEither[E, L, B], + c ReaderEither[E, L, C], + d ReaderEither[E, L, D], +) ReaderEither[E, L, T.Tuple4[A, B, C, D]] { + return G.SequenceT4[ + ReaderEither[E, L, A], + ReaderEither[E, L, B], + ReaderEither[E, L, C], + ReaderEither[E, L, D], + ReaderEither[E, L, T.Tuple4[A, B, C, D]], + ](a, b, c, d) +} diff --git a/v2/readereither/sequence_test.go b/v2/readereither/sequence_test.go new file mode 100644 index 0000000..bfbaa16 --- /dev/null +++ b/v2/readereither/sequence_test.go @@ -0,0 +1,92 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readereither + +import ( + "fmt" + "testing" + + E "github.com/IBM/fp-go/v2/either" + T "github.com/IBM/fp-go/v2/tuple" + "github.com/stretchr/testify/assert" +) + +var ( + errFoo = fmt.Errorf("error") +) + +func TestSequenceT1(t *testing.T) { + + t1 := Of[MyContext, error]("s1") + e1 := Left[MyContext, string](errFoo) + + res1 := SequenceT1(t1) + assert.Equal(t, E.Of[error](T.MakeTuple1("s1")), res1(defaultContext)) + + res2 := SequenceT1(e1) + assert.Equal(t, E.Left[T.Tuple1[string]](errFoo), res2(defaultContext)) +} + +func TestSequenceT2(t *testing.T) { + + t1 := Of[MyContext, error]("s1") + e1 := Left[MyContext, string](errFoo) + t2 := Of[MyContext, error](2) + e2 := Left[MyContext, int](errFoo) + + res1 := SequenceT2(t1, t2) + assert.Equal(t, E.Of[error](T.MakeTuple2("s1", 2)), res1(defaultContext)) + + res2 := SequenceT2(e1, t2) + assert.Equal(t, E.Left[T.Tuple2[string, int]](errFoo), res2(defaultContext)) + + res3 := SequenceT2(t1, e2) + assert.Equal(t, E.Left[T.Tuple2[string, int]](errFoo), res3(defaultContext)) +} + +func TestSequenceT3(t *testing.T) { + + t1 := Of[MyContext, error]("s1") + e1 := Left[MyContext, string](errFoo) + t2 := Of[MyContext, error](2) + e2 := Left[MyContext, int](errFoo) + t3 := Of[MyContext, error](true) + e3 := Left[MyContext, bool](errFoo) + + res1 := SequenceT3(t1, t2, t3) + assert.Equal(t, E.Of[error](T.MakeTuple3("s1", 2, true)), res1(defaultContext)) + + res2 := SequenceT3(e1, t2, t3) + assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](errFoo), res2(defaultContext)) + + res3 := SequenceT3(t1, e2, t3) + assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](errFoo), res3(defaultContext)) + + res4 := SequenceT3(t1, t2, e3) + assert.Equal(t, E.Left[T.Tuple3[string, int, bool]](errFoo), res4(defaultContext)) +} + +func TestSequenceT4(t *testing.T) { + + t1 := Of[MyContext, error]("s1") + t2 := Of[MyContext, error](2) + t3 := Of[MyContext, error](true) + t4 := Of[MyContext, error](1.0) + + res := SequenceT4(t1, t2, t3, t4) + + assert.Equal(t, E.Of[error](T.MakeTuple4("s1", 2, true, 1.0)), res(defaultContext)) +} diff --git a/v2/readerio/ap.go b/v2/readerio/ap.go new file mode 100644 index 0000000..31e1ac1 --- /dev/null +++ b/v2/readerio/ap.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// MonadApFirst combines two effectful actions, keeping only the result of the first. +func MonadApFirst[A, R, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, A] { + return G.MonadApFirst[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) A]](first, second) +} + +// ApFirst combines two effectful actions, keeping only the result of the first. +func ApFirst[A, R, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, A] { + return G.ApFirst[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) A]](second) +} + +// MonadApSecond combines two effectful actions, keeping only the result of the second. +func MonadApSecond[A, R, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, B] { + return G.MonadApSecond[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) B]](first, second) +} + +// ApSecond combines two effectful actions, keeping only the result of the second. +func ApSecond[A, R, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, B] { + return G.ApSecond[ReaderIO[R, A], ReaderIO[R, B], ReaderIO[R, func(B) B]](second) +} diff --git a/v2/readerio/array.go b/v2/readerio/array.go new file mode 100644 index 0000000..948a6b4 --- /dev/null +++ b/v2/readerio/array.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + IO "github.com/IBM/fp-go/v2/io" + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// TraverseArray transforms an array +func TraverseArray[R, A, B any](f func(A) ReaderIO[R, B]) func([]A) ReaderIO[R, []B] { + return G.TraverseArray[ReaderIO[R, B], ReaderIO[R, []B], IO.IO[B], IO.IO[[]B], []A](f) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[R, A, B any](f func(int, A) ReaderIO[R, B]) func([]A) ReaderIO[R, []B] { + return G.TraverseArrayWithIndex[ReaderIO[R, B], ReaderIO[R, []B], IO.IO[B], IO.IO[[]B], []A](f) +} + +// SequenceArray converts a homogeneous sequence of Readers into a Reader of sequence +func SequenceArray[R, A any](ma []ReaderIO[R, A]) ReaderIO[R, []A] { + return G.SequenceArray[ReaderIO[R, A], ReaderIO[R, []A]](ma) +} diff --git a/v2/readerio/array_test.go b/v2/readerio/array_test.go new file mode 100644 index 0000000..6f133e5 --- /dev/null +++ b/v2/readerio/array_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + "context" + "testing" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + "github.com/stretchr/testify/assert" +) + +func TestTraverseArray(t *testing.T) { + f := TraverseArray(func(a string) ReaderIO[context.Context, string] { + return Of[context.Context](a + a) + }) + ctx := context.Background() + assert.Equal(t, A.Empty[string](), F.Pipe1(A.Empty[string](), f)(ctx)()) + assert.Equal(t, []string{"aa", "bb"}, F.Pipe1([]string{"a", "b"}, f)(ctx)()) +} diff --git a/v2/readerio/bind.go b/v2/readerio/bind.go new file mode 100644 index 0000000..646675a --- /dev/null +++ b/v2/readerio/bind.go @@ -0,0 +1,67 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + IO "github.com/IBM/fp-go/v2/io" + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[R, S any]( + empty S, +) ReaderIO[R, S] { + return G.Do[ReaderIO[R, S], IO.IO[S], R, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) ReaderIO[R, T], +) func(ReaderIO[R, S1]) ReaderIO[R, S2] { + return G.Bind[ReaderIO[R, S1], ReaderIO[R, S2], ReaderIO[R, T], IO.IO[S1], IO.IO[S2], IO.IO[T], R, S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(ReaderIO[R, S1]) ReaderIO[R, S2] { + return G.Let[ReaderIO[R, S1], ReaderIO[R, S2], IO.IO[S1], IO.IO[S2], R, S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[R, S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(ReaderIO[R, S1]) ReaderIO[R, S2] { + return G.LetTo[ReaderIO[R, S1], ReaderIO[R, S2], IO.IO[S1], IO.IO[S2], R, S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[R, S1, T any]( + setter func(T) S1, +) func(ReaderIO[R, T]) ReaderIO[R, S1] { + return G.BindTo[ReaderIO[R, S1], ReaderIO[R, T], IO.IO[S1], IO.IO[T], R, S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa ReaderIO[R, T], +) func(ReaderIO[R, S1]) ReaderIO[R, S2] { + return G.ApS[ReaderIO[R, func(T) S2], ReaderIO[R, S1], ReaderIO[R, S2], ReaderIO[R, T], IO.IO[func(T) S2], IO.IO[S1], IO.IO[S2], IO.IO[T], R, S1, S2, T](setter, fa) +} diff --git a/v2/readerio/bind_test.go b/v2/readerio/bind_test.go new file mode 100644 index 0000000..af67185 --- /dev/null +++ b/v2/readerio/bind_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + "context" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) ReaderIO[context.Context, string] { + return Of[context.Context]("Doe") +} + +func getGivenName(s utils.WithLastName) ReaderIO[context.Context, string] { + return Of[context.Context]("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do[context.Context](utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map[context.Context](utils.GetFullName), + ) + + assert.Equal(t, res(context.Background())(), "John Doe") +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do[context.Context](utils.Empty), + ApS(utils.SetLastName, Of[context.Context]("Doe")), + ApS(utils.SetGivenName, Of[context.Context]("John")), + Map[context.Context](utils.GetFullName), + ) + + assert.Equal(t, res(context.Background())(), "John Doe") +} diff --git a/v2/readerio/eq.go b/v2/readerio/eq.go new file mode 100644 index 0000000..9eba6ed --- /dev/null +++ b/v2/readerio/eq.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// Eq implements the equals predicate for values contained in the IO monad +func Eq[R, A any](e EQ.Eq[A]) func(r R) EQ.Eq[ReaderIO[R, A]] { + return G.Eq[ReaderIO[R, A]](e) +} diff --git a/v2/readerio/from.go b/v2/readerio/from.go new file mode 100644 index 0000000..6efafd4 --- /dev/null +++ b/v2/readerio/from.go @@ -0,0 +1,40 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + IO "github.com/IBM/fp-go/v2/io" + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// these functions From a golang function with the context as the firsr parameter into a either reader with the context as the last parameter +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func From0[R, A any](f func(R) IO.IO[A]) func() ReaderIO[R, A] { + return G.From0[ReaderIO[R, A]](f) +} + +func From1[R, T1, A any](f func(R, T1) IO.IO[A]) func(T1) ReaderIO[R, A] { + return G.From1[ReaderIO[R, A]](f) +} + +func From2[R, T1, T2, A any](f func(R, T1, T2) IO.IO[A]) func(T1, T2) ReaderIO[R, A] { + return G.From2[ReaderIO[R, A]](f) +} + +func From3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) IO.IO[A]) func(T1, T2, T3) ReaderIO[R, A] { + return G.From3[ReaderIO[R, A]](f) +} diff --git a/v2/readerio/generic/ap.go b/v2/readerio/generic/ap.go new file mode 100644 index 0000000..8132a66 --- /dev/null +++ b/v2/readerio/generic/ap.go @@ -0,0 +1,62 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + G "github.com/IBM/fp-go/v2/internal/apply" +) + +// MonadApFirst combines two effectful actions, keeping only the result of the first. +func MonadApFirst[GRA ~func(R) GA, GRB ~func(R) GB, GRBA ~func(R) GBA, GA ~func() A, GB ~func() B, GBA ~func() func(B) A, R, A, B any](first GRA, second GRB) GRA { + return G.MonadApFirst( + MonadAp[GRB, GRA, GRBA, GB, GA, GBA, R, B, A], + MonadMap[GRA, GRBA, GA, GBA, R, A, func(B) A], + + first, + second, + ) +} + +// ApFirst combines two effectful actions, keeping only the result of the first. +func ApFirst[GRA ~func(R) GA, GRB ~func(R) GB, GRBA ~func(R) GBA, GA ~func() A, GB ~func() B, GBA ~func() func(B) A, R, A, B any](second GRB) func(GRA) GRA { + return G.ApFirst( + MonadAp[GRB, GRA, GRBA, GB, GA, GBA, R, B, A], + MonadMap[GRA, GRBA, GA, GBA, R, A, func(B) A], + + second, + ) +} + +// MonadApSecond combines two effectful actions, keeping only the result of the second. +func MonadApSecond[GRA ~func(R) GA, GRB ~func(R) GB, GRBB ~func(R) GBB, GA ~func() A, GB ~func() B, GBB ~func() func(B) B, R, A, B any](first GRA, second GRB) GRB { + return G.MonadApSecond( + MonadAp[GRB, GRB, GRBB, GB, GB, GBB, R, B, B], + MonadMap[GRA, GRBB, GA, GBB, R, A, func(B) B], + + first, + second, + ) +} + +// ApSecond combines two effectful actions, keeping only the result of the second. +func ApSecond[GRA ~func(R) GA, GRB ~func(R) GB, GRBB ~func(R) GBB, GA ~func() A, GB ~func() B, GBB ~func() func(B) B, R, A, B any](second GRB) func(GRA) GRB { + return G.ApSecond( + MonadAp[GRB, GRB, GRBB, GB, GB, GBB, R, B, B], + MonadMap[GRA, GRBB, GA, GBB, R, A, func(B) B], + + second, + ) +} diff --git a/v2/readerio/generic/array.go b/v2/readerio/generic/array.go new file mode 100644 index 0000000..edcc6d4 --- /dev/null +++ b/v2/readerio/generic/array.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" +) + +// MonadTraverseArray transforms an array +func MonadTraverseArray[GB ~func(E) GIOB, GBS ~func(E) GIOBS, GIOB ~func() B, GIOBS ~func() BBS, AAS ~[]A, BBS ~[]B, E, A, B any](ma AAS, f func(A) GB) GBS { + return RA.MonadTraverse[AAS]( + Of[GBS, GIOBS, E, BBS], + Map[GBS, func(E) func() func(B) BBS, GIOBS, func() func(B) BBS, E, BBS, func(B) BBS], + Ap[GB, GBS, func(E) func() func(B) BBS, GIOB, GIOBS, func() func(B) BBS, E, B, BBS], + + ma, f, + ) +} + +// TraverseArray transforms an array +func TraverseArray[GB ~func(E) GIOB, GBS ~func(E) GIOBS, GIOB ~func() B, GIOBS ~func() BBS, AAS ~[]A, BBS ~[]B, E, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, GIOBS, E, BBS], + Map[GBS, func(E) func() func(B) BBS, GIOBS, func() func(B) BBS, E, BBS, func(B) BBS], + Ap[GB, GBS, func(E) func() func(B) BBS, GIOB, GIOBS, func() func(B) BBS, E, B, BBS], + + f, + ) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[GB ~func(E) GIOB, GBS ~func(E) GIOBS, GIOB ~func() B, GIOBS ~func() BBS, AAS ~[]A, BBS ~[]B, E, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, GIOBS, E, BBS], + Map[GBS, func(E) func() func(B) BBS, GIOBS, func() func(B) BBS, E, BBS, func(B) BBS], + Ap[GB, GBS, func(E) func() func(B) BBS, GIOB, GIOBS, func() func(B) BBS, E, B, BBS], + + f, + ) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[GA ~func(E) GIOA, GAS ~func(E) GIOAS, GIOA ~func() A, GIOAS ~func() AAS, AAS ~[]A, GAAS ~[]GA, E, A any](ma GAAS) GAS { + return MonadTraverseArray[GA, GAS](ma, F.Identity[GA]) +} diff --git a/v2/readerio/generic/bind.go b/v2/readerio/generic/bind.go new file mode 100644 index 0000000..ba5a11d --- /dev/null +++ b/v2/readerio/generic/bind.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GRS ~func(R) GS, GS ~func() S, R, S any]( + empty S, +) GRS { + return Of[GRS, GS, R, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, R, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GRT, +) func(GRS1) GRS2 { + return C.Bind( + Chain[GRS1, GRS2, GS1, GS2, R, S1, S2], + Map[GRT, GRS2, GT, GS2, R, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GS1 ~func() S1, GS2 ~func() S2, R, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GRS1) GRS2 { + return F.Let( + Map[GRS1, GRS2, GS1, GS2, R, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GS1 ~func() S1, GS2 ~func() S2, R, S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GRS1) GRS2 { + return F.LetTo( + Map[GRS1, GRS2, GS1, GS2, R, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GRS1 ~func(R) GS1, GRT ~func(R) GT, GS1 ~func() S1, GT ~func() T, R, S1, T any]( + setter func(T) S1, +) func(GRT) GRS1 { + return C.BindTo( + Map[GRT, GRS1, GT, GS1, R, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GRTS1 ~func(R) GTS1, GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GTS1 ~func() func(T) S2, GS1 ~func() S1, GS2 ~func() S2, GT ~func() T, R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa GRT, +) func(GRS1) GRS2 { + return A.ApS( + Ap[GRT, GRS2, GRTS1, GT, GS2, GTS1, R, T, S2], + Map[GRS1, GRTS1, GS1, GTS1, R, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/readerio/generic/eq.go b/v2/readerio/generic/eq.go new file mode 100644 index 0000000..7ba96f4 --- /dev/null +++ b/v2/readerio/generic/eq.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/internal/eq" +) + +// Eq implements the equals predicate for values contained in the IO monad +func Eq[GEA ~func(R) GIOA, GIOA ~func() A, R, A any](e EQ.Eq[A]) func(r R) EQ.Eq[GEA] { + // comparator for the monad + eq := G.Eq( + MonadMap[GEA, func(R) func() func(A) bool, GIOA, func() func(A) bool, R, A, func(A) bool], + MonadAp[GEA, func(R) func() bool, func(R) func() func(A) bool], + e, + ) + // eagerly execute + return func(ctx R) EQ.Eq[GEA] { + return EQ.FromEquals(func(l, r GEA) bool { + return eq(l, r)(ctx)() + }) + } +} diff --git a/v2/readerio/generic/from.go b/v2/readerio/generic/from.go new file mode 100644 index 0000000..cca97a6 --- /dev/null +++ b/v2/readerio/generic/from.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + G "github.com/IBM/fp-go/v2/reader/generic" +) + +// these functions From a golang function with the context as the firsr parameter into a either reader with the context as the last parameter +// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention + +func From0[GEA ~func(R) GIOA, GIOA ~func() A, R, A any](f func(R) GIOA) func() GEA { + return G.From0[GEA](f) +} + +func From1[GEA ~func(R) GIOA, GIOA ~func() A, R, T1, A any](f func(R, T1) GIOA) func(T1) GEA { + return G.From1[GEA](f) +} + +func From2[GEA ~func(R) GIOA, GIOA ~func() A, R, T1, T2, A any](f func(R, T1, T2) GIOA) func(T1, T2) GEA { + return G.From2[GEA](f) +} + +func From3[GEA ~func(R) GIOA, GIOA ~func() A, R, T1, T2, T3, A any](f func(R, T1, T2, T3) GIOA) func(T1, T2, T3) GEA { + return G.From3[GEA](f) +} diff --git a/v2/readerio/generic/reader.go b/v2/readerio/generic/reader.go new file mode 100644 index 0000000..c0169e6 --- /dev/null +++ b/v2/readerio/generic/reader.go @@ -0,0 +1,166 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "sync" + + F "github.com/IBM/fp-go/v2/function" + FIO "github.com/IBM/fp-go/v2/internal/fromio" + FR "github.com/IBM/fp-go/v2/internal/fromreader" + FC "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/readert" + IO "github.com/IBM/fp-go/v2/io/generic" + R "github.com/IBM/fp-go/v2/reader/generic" +) + +func FromIO[GEA ~func(E) GIOA, GIOA ~func() A, E, A any](t GIOA) GEA { + return R.Of[GEA, E](t) +} + +func FromReader[GA ~func(E) A, GEA ~func(E) GIOA, GIOA ~func() A, E, A any](r GA) GEA { + return readert.MonadFromReader[GA, GEA](IO.Of[GIOA, A], r) +} + +func MonadMap[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](fa GEA, f func(A) B) GEB { + return readert.MonadMap[GEA, GEB](IO.MonadMap[GIOA, GIOB, A, B], fa, f) +} + +func Map[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) B) func(GEA) GEB { + return readert.Map[GEA, GEB](IO.Map[GIOA, GIOB, A, B], f) +} + +func MonadChain[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](ma GEA, f func(A) GEB) GEB { + return readert.MonadChain(IO.MonadChain[GIOA, GIOB, A, B], ma, f) +} + +func Chain[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GEB) func(GEA) GEB { + return F.Bind2nd(MonadChain[GEA, GEB, GIOA, GIOB, E, A, B], f) +} + +func Of[GEA ~func(E) GIOA, GIOA ~func() A, E, A any](a A) GEA { + return readert.MonadOf[GEA](IO.Of[GIOA, A], a) +} + +func MonadAp[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GEFAB ~func(E) GIOFAB, GIOA ~func() A, GIOB ~func() B, GIOFAB ~func() func(A) B, E, A, B any](fab GEFAB, fa GEA) GEB { + return readert.MonadAp[GEA, GEB, GEFAB, E, A](IO.MonadAp[GIOA, GIOB, GIOFAB, A, B], fab, fa) +} + +func Ap[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GEFAB ~func(E) GIOFAB, GIOA ~func() A, GIOB ~func() B, GIOFAB ~func() func(A) B, E, A, B any](fa GEA) func(GEFAB) GEB { + return F.Bind2nd(MonadAp[GEA, GEB, GEFAB, GIOA, GIOB, GIOFAB, E, A, B], fa) +} + +func MonadApSeq[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GEFAB ~func(E) GIOFAB, GIOA ~func() A, GIOB ~func() B, GIOFAB ~func() func(A) B, E, A, B any](fab GEFAB, fa GEA) GEB { + return readert.MonadAp[GEA, GEB, GEFAB, E, A](IO.MonadApSeq[GIOA, GIOB, GIOFAB, A, B], fab, fa) +} + +func ApSeq[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GEFAB ~func(E) GIOFAB, GIOA ~func() A, GIOB ~func() B, GIOFAB ~func() func(A) B, E, A, B any](fa GEA) func(GEFAB) GEB { + return F.Bind2nd(MonadApSeq[GEA, GEB, GEFAB, GIOA, GIOB, GIOFAB, E, A, B], fa) +} + +func MonadApPar[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GEFAB ~func(E) GIOFAB, GIOA ~func() A, GIOB ~func() B, GIOFAB ~func() func(A) B, E, A, B any](fab GEFAB, fa GEA) GEB { + return readert.MonadAp[GEA, GEB, GEFAB, E, A](IO.MonadApPar[GIOA, GIOB, GIOFAB, A, B], fab, fa) +} + +func ApPar[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GEFAB ~func(E) GIOFAB, GIOA ~func() A, GIOB ~func() B, GIOFAB ~func() func(A) B, E, A, B any](fa GEA) func(GEFAB) GEB { + return F.Bind2nd(MonadApPar[GEA, GEB, GEFAB, GIOA, GIOB, GIOFAB, E, A, B], fa) +} + +func Ask[GEE ~func(E) GIOE, GIOE ~func() E, E any]() GEE { + return FR.Ask(FromReader[func(E) E, GEE, GIOE, E, E])() +} + +func Asks[GA ~func(E) A, GEA ~func(E) GIOA, GIOA ~func() A, E, A any](r GA) GEA { + return FR.Asks(FromReader[GA, GEA, GIOA, E, A])(r) +} + +func MonadChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](ma GEA, f func(A) GIOB) GEB { + return FIO.MonadChainIOK( + MonadChain[GEA, GEB], + FromIO[GEB], + ma, f, + ) +} + +func ChainIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GEA) GEB { + return FIO.ChainIOK( + Chain[GEA, GEB], + FromIO[GEB], + f, + ) +} + +func MonadChainFirstIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](ma GEA, f func(A) GIOB) GEA { + return FIO.MonadChainFirstIOK( + MonadChain[GEA, GEA], + MonadMap[GEB, GEA], + FromIO[GEB], + ma, f, + ) +} + +func ChainFirstIOK[GEA ~func(E) GIOA, GEB ~func(E) GIOB, GIOA ~func() A, GIOB ~func() B, E, A, B any](f func(A) GIOB) func(GEA) GEA { + return FIO.ChainFirstIOK( + Chain[GEA, GEA], + Map[GEB, GEA], + FromIO[GEB], + f, + ) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[GEA ~func(E) GA, GA ~func() A, E, A any](gen func() GEA) GEA { + return func(e E) GA { + return func() A { + return gen()(e)() + } + } +} + +// Memoize computes the value of the provided reader monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[GEA ~func(E) GA, GA ~func() A, E, A any](rdr GEA) GEA { + // synchronization primitives + var once sync.Once + var result A + // callback + gen := func(e E) func() { + return func() { + result = rdr(e)() + } + } + // returns our memoized wrapper + return func(e E) GA { + io := gen(e) + return func() A { + once.Do(io) + return result + } + } +} + +func Flatten[GEA ~func(R) GIOA, GGEA ~func(R) GIOEA, GIOA ~func() A, GIOEA ~func() GEA, R, A any](mma GGEA) GEA { + return MonadChain(mma, F.Identity[GEA]) +} + +func MonadFlap[GEFAB ~func(E) GIOFAB, GEB ~func(E) GIOB, GIOFAB ~func() func(A) B, GIOB ~func() B, E, A, B any](fab GEFAB, a A) GEB { + return FC.MonadFlap(MonadMap[GEFAB, GEB], fab, a) +} + +func Flap[GEFAB ~func(E) GIOFAB, GEB ~func(E) GIOB, GIOFAB ~func() func(A) B, GIOB ~func() B, E, A, B any](a A) func(GEFAB) GEB { + return FC.Flap(Map[GEFAB, GEB], a) +} diff --git a/v2/readerio/generic/sequence.go b/v2/readerio/generic/sequence.go new file mode 100644 index 0000000..dc5f383 --- /dev/null +++ b/v2/readerio/generic/sequence.go @@ -0,0 +1,93 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[ + GA ~func(E) GIOA, + GTA ~func(E) GIOTA, + GIOA ~func() A, + GIOTA ~func() T.Tuple1[A], + E, A any](a GA) GTA { + return apply.SequenceT1( + Map[GA, GTA, GIOA, GIOTA, E, A, T.Tuple1[A]], + + a, + ) +} + +func SequenceT2[ + GA ~func(E) GIOA, + GB ~func(E) GIOB, + GTAB ~func(E) GIOTAB, + GIOA ~func() A, + GIOB ~func() B, + GIOTAB ~func() T.Tuple2[A, B], + E, A, B any](a GA, b GB) GTAB { + return apply.SequenceT2( + Map[GA, func(E) func() func(B) T.Tuple2[A, B], GIOA, func() func(B) T.Tuple2[A, B], E, A, func(B) T.Tuple2[A, B]], + Ap[GB, GTAB, func(E) func() func(B) T.Tuple2[A, B], GIOB, GIOTAB, func() func(B) T.Tuple2[A, B], E, B, T.Tuple2[A, B]], + + a, b, + ) +} + +func SequenceT3[ + GA ~func(E) GIOA, + GB ~func(E) GIOB, + GC ~func(E) GIOC, + GTABC ~func(E) GIOTABC, + GIOA ~func() A, + GIOB ~func() B, + GIOC ~func() C, + GIOTABC ~func() T.Tuple3[A, B, C], + E, A, B, C any](a GA, b GB, c GC) GTABC { + return apply.SequenceT3( + Map[GA, func(E) func() func(B) func(C) T.Tuple3[A, B, C], GIOA, func() func(B) func(C) T.Tuple3[A, B, C], E, A, func(B) func(C) T.Tuple3[A, B, C]], + Ap[GB, func(E) func() func(C) T.Tuple3[A, B, C], func(E) func() func(B) func(C) T.Tuple3[A, B, C], GIOB, func() func(C) T.Tuple3[A, B, C], func() func(B) func(C) T.Tuple3[A, B, C], E, B, func(C) T.Tuple3[A, B, C]], + Ap[GC, GTABC, func(E) func() func(C) T.Tuple3[A, B, C], GIOC, GIOTABC, func() func(C) T.Tuple3[A, B, C], E, C, T.Tuple3[A, B, C]], + + a, b, c, + ) +} + +func SequenceT4[ + GA ~func(E) GIOA, + GB ~func(E) GIOB, + GC ~func(E) GIOC, + GD ~func(E) GIOD, + GTABCD ~func(E) GIOTABCD, + GIOA ~func() A, + GIOB ~func() B, + GIOC ~func() C, + GIOD ~func() D, + GIOTABCD ~func() T.Tuple4[A, B, C, D], + E, A, B, C, D any](a GA, b GB, c GC, d GD) GTABCD { + return apply.SequenceT4( + Map[GA, func(E) func() func(B) func(C) func(D) T.Tuple4[A, B, C, D], GIOA, func() func(B) func(C) func(D) T.Tuple4[A, B, C, D], E, A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], + Ap[GB, func(E) func() func(C) func(D) T.Tuple4[A, B, C, D], func(E) func() func(B) func(C) func(D) T.Tuple4[A, B, C, D], GIOB, func() func(C) func(D) T.Tuple4[A, B, C, D], func() func(B) func(C) func(D) T.Tuple4[A, B, C, D], E, B, func(C) func(D) T.Tuple4[A, B, C, D]], + Ap[GC, func(E) func() func(D) T.Tuple4[A, B, C, D], func(E) func() func(C) func(D) T.Tuple4[A, B, C, D], GIOC, func() func(D) T.Tuple4[A, B, C, D], func() func(C) func(D) T.Tuple4[A, B, C, D], E, C, func(D) T.Tuple4[A, B, C, D]], + Ap[GD, GTABCD, func(E) func() func(D) T.Tuple4[A, B, C, D], GIOD, GIOTABCD, func() func(D) T.Tuple4[A, B, C, D], E, D, T.Tuple4[A, B, C, D]], + + a, b, c, d, + ) +} diff --git a/v2/readerio/generic/sync.go b/v2/readerio/generic/sync.go new file mode 100644 index 0000000..9c397e2 --- /dev/null +++ b/v2/readerio/generic/sync.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + F "github.com/IBM/fp-go/v2/function" + G "github.com/IBM/fp-go/v2/io/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[GEA ~func(R) GIOA, GIOA ~func() A, R, A any](lock func() context.CancelFunc) func(fa GEA) GEA { + l := G.WithLock[GIOA](lock) + return func(fa GEA) GEA { + return F.Flow2( + fa, + l, + ) + } +} diff --git a/v2/readerio/reader.go b/v2/readerio/reader.go new file mode 100644 index 0000000..c8376fb --- /dev/null +++ b/v2/readerio/reader.go @@ -0,0 +1,97 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + IO "github.com/IBM/fp-go/v2/io" + R "github.com/IBM/fp-go/v2/reader" + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +type ReaderIO[E, A any] R.Reader[E, IO.IO[A]] + +// FromIO converts an [IO.IO] to a [ReaderIO] +func FromIO[E, A any](t IO.IO[A]) ReaderIO[E, A] { + return G.FromIO[ReaderIO[E, A]](t) +} + +func MonadMap[E, A, B any](fa ReaderIO[E, A], f func(A) B) ReaderIO[E, B] { + return G.MonadMap[ReaderIO[E, A], ReaderIO[E, B]](fa, f) +} + +func Map[E, A, B any](f func(A) B) func(ReaderIO[E, A]) ReaderIO[E, B] { + return G.Map[ReaderIO[E, A], ReaderIO[E, B]](f) +} + +func MonadChain[E, A, B any](ma ReaderIO[E, A], f func(A) ReaderIO[E, B]) ReaderIO[E, B] { + return G.MonadChain(ma, f) +} + +func Chain[E, A, B any](f func(A) ReaderIO[E, B]) func(ReaderIO[E, A]) ReaderIO[E, B] { + return G.Chain[ReaderIO[E, A]](f) +} + +func Of[E, A any](a A) ReaderIO[E, A] { + return G.Of[ReaderIO[E, A]](a) +} + +func MonadAp[B, E, A any](fab ReaderIO[E, func(A) B], fa ReaderIO[E, A]) ReaderIO[E, B] { + return G.MonadAp[ReaderIO[E, A], ReaderIO[E, B]](fab, fa) +} + +func Ap[B, E, A any](fa ReaderIO[E, A]) func(ReaderIO[E, func(A) B]) ReaderIO[E, B] { + return G.Ap[ReaderIO[E, A], ReaderIO[E, B], ReaderIO[E, func(A) B]](fa) +} + +func Ask[E any]() ReaderIO[E, E] { + return G.Ask[ReaderIO[E, E]]() +} + +func Asks[E, A any](r R.Reader[E, A]) ReaderIO[E, A] { + return G.Asks[R.Reader[E, A], ReaderIO[E, A]](r) +} + +func MonadChainIOK[E, A, B any](ma ReaderIO[E, A], f func(A) IO.IO[B]) ReaderIO[E, B] { + return G.MonadChainIOK[ReaderIO[E, A], ReaderIO[E, B]](ma, f) +} + +func ChainIOK[E, A, B any](f func(A) IO.IO[B]) func(ReaderIO[E, A]) ReaderIO[E, B] { + return G.ChainIOK[ReaderIO[E, A], ReaderIO[E, B]](f) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[E, A any](gen func() ReaderIO[E, A]) ReaderIO[E, A] { + return G.Defer[ReaderIO[E, A]](gen) +} + +// Memoize computes the value of the provided [ReaderIO] monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[E, A any](rdr ReaderIO[E, A]) ReaderIO[E, A] { + return G.Memoize[ReaderIO[E, A]](rdr) +} + +func Flatten[E, A any](mma ReaderIO[E, ReaderIO[E, A]]) ReaderIO[E, A] { + return G.Flatten[ReaderIO[E, A], ReaderIO[E, ReaderIO[E, A]]](mma) +} + +func MonadFlap[E, A, B any](fab ReaderIO[E, func(A) B], a A) ReaderIO[E, B] { + return G.MonadFlap[ReaderIO[E, func(A) B], ReaderIO[E, B]](fab, a) +} + +func Flap[E, A, B any](a A) func(ReaderIO[E, func(A) B]) ReaderIO[E, B] { + return G.Flap[ReaderIO[E, func(A) B], ReaderIO[E, B]](a) +} diff --git a/v2/readerio/reader_test.go b/v2/readerio/reader_test.go new file mode 100644 index 0000000..18ae16d --- /dev/null +++ b/v2/readerio/reader_test.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + "context" + "testing" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func TestMap(t *testing.T) { + + g := F.Pipe1( + Of[context.Context](1), + Map[context.Context](utils.Double), + ) + + assert.Equal(t, 2, g(context.Background())()) +} + +func TestAp(t *testing.T) { + g := F.Pipe1( + Of[context.Context](utils.Double), + Ap[int](Of[context.Context](1)), + ) + + assert.Equal(t, 2, g(context.Background())()) +} diff --git a/v2/readerio/sequence.go b/v2/readerio/sequence.go new file mode 100644 index 0000000..5c86fbd --- /dev/null +++ b/v2/readerio/sequence.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + G "github.com/IBM/fp-go/v2/readerio/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[R, A any](a ReaderIO[R, A]) ReaderIO[R, T.Tuple1[A]] { + return G.SequenceT1[ + ReaderIO[R, A], + ReaderIO[R, T.Tuple1[A]], + ](a) +} + +func SequenceT2[R, A, B any](a ReaderIO[R, A], b ReaderIO[R, B]) ReaderIO[R, T.Tuple2[A, B]] { + return G.SequenceT2[ + ReaderIO[R, A], + ReaderIO[R, B], + ReaderIO[R, T.Tuple2[A, B]], + ](a, b) +} + +func SequenceT3[R, A, B, C any](a ReaderIO[R, A], b ReaderIO[R, B], c ReaderIO[R, C]) ReaderIO[R, T.Tuple3[A, B, C]] { + return G.SequenceT3[ + ReaderIO[R, A], + ReaderIO[R, B], + ReaderIO[R, C], + ReaderIO[R, T.Tuple3[A, B, C]], + ](a, b, c) +} + +func SequenceT4[R, A, B, C, D any](a ReaderIO[R, A], b ReaderIO[R, B], c ReaderIO[R, C], d ReaderIO[R, D]) ReaderIO[R, T.Tuple4[A, B, C, D]] { + return G.SequenceT4[ + ReaderIO[R, A], + ReaderIO[R, B], + ReaderIO[R, C], + ReaderIO[R, D], + ReaderIO[R, T.Tuple4[A, B, C, D]], + ](a, b, c, d) +} diff --git a/v2/readerio/sync.go b/v2/readerio/sync.go new file mode 100644 index 0000000..9bb0e94 --- /dev/null +++ b/v2/readerio/sync.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerio + +import ( + "context" + + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[R, A any](lock func() context.CancelFunc) func(fa ReaderIO[R, A]) ReaderIO[R, A] { + return G.WithLock[ReaderIO[R, A]](lock) +} diff --git a/v2/readerioeither/array_test.go b/v2/readerioeither/array_test.go new file mode 100644 index 0000000..f3cfb80 --- /dev/null +++ b/v2/readerioeither/array_test.go @@ -0,0 +1,71 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + "fmt" + "testing" + + A "github.com/IBM/fp-go/v2/array" + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + TST "github.com/IBM/fp-go/v2/internal/testing" + "github.com/stretchr/testify/assert" +) + +func TestTraverseArray(t *testing.T) { + f := TraverseArray(func(a string) ReaderIOEither[context.Context, string, string] { + if len(a) > 0 { + return Right[context.Context, string](a + a) + } + return Left[context.Context, string, string]("e") + }) + ctx := context.Background() + assert.Equal(t, ET.Right[string](A.Empty[string]()), F.Pipe1(A.Empty[string](), f)(ctx)()) + assert.Equal(t, ET.Right[string]([]string{"aa", "bb"}), F.Pipe1([]string{"a", "b"}, f)(ctx)()) + assert.Equal(t, ET.Left[[]string]("e"), F.Pipe1([]string{"a", ""}, f)(ctx)()) +} + +func TestSequenceArray(t *testing.T) { + + s := TST.SequenceArrayTest( + FromStrictEquals[context.Context, error, bool]()(context.Background()), + Pointed[context.Context, error, string](), + Pointed[context.Context, error, bool](), + Functor[context.Context, error, []string, bool](), + SequenceArray[context.Context, error, string], + ) + + for i := 0; i < 10; i++ { + t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i)) + } +} + +func TestSequenceArrayError(t *testing.T) { + + s := TST.SequenceArrayErrorTest( + FromStrictEquals[context.Context, error, bool]()(context.Background()), + Left[context.Context, string, error], + Left[context.Context, bool, error], + Pointed[context.Context, error, string](), + Pointed[context.Context, error, bool](), + Functor[context.Context, error, []string, bool](), + SequenceArray[context.Context, error, string], + ) + // run across four bits + s(4)(t) +} diff --git a/v2/readerioeither/bind.go b/v2/readerioeither/bind.go new file mode 100644 index 0000000..7ec0277 --- /dev/null +++ b/v2/readerioeither/bind.go @@ -0,0 +1,67 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + IOE "github.com/IBM/fp-go/v2/ioeither" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[R, E, S any]( + empty S, +) ReaderIOEither[R, E, S] { + return G.Do[ReaderIOEither[R, E, S], IOE.IOEither[E, S], R, E, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) ReaderIOEither[R, E, T], +) func(ReaderIOEither[R, E, S1]) ReaderIOEither[R, E, S2] { + return G.Bind[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], ReaderIOEither[R, E, T], IOE.IOEither[E, S1], IOE.IOEither[E, S2], IOE.IOEither[E, T], R, E, S1, S2, T](setter, f) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(ReaderIOEither[R, E, S1]) ReaderIOEither[R, E, S2] { + return G.Let[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], R, E, S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + b T, +) func(ReaderIOEither[R, E, S1]) ReaderIOEither[R, E, S2] { + return G.LetTo[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], R, E, S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[R, E, S1, T any]( + setter func(T) S1, +) func(ReaderIOEither[R, E, T]) ReaderIOEither[R, E, S1] { + return G.BindTo[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, T], IOE.IOEither[E, S1], IOE.IOEither[E, T], R, E, S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa ReaderIOEither[R, E, T], +) func(ReaderIOEither[R, E, S1]) ReaderIOEither[R, E, S2] { + return G.ApS[ReaderIOEither[R, E, func(T) S2], ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], ReaderIOEither[R, E, T], IOE.IOEither[E, func(T) S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], IOE.IOEither[E, T], R, E, S1, S2, T](setter, fa) +} diff --git a/v2/readerioeither/bind_test.go b/v2/readerioeither/bind_test.go new file mode 100644 index 0000000..9f51854 --- /dev/null +++ b/v2/readerioeither/bind_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +func getLastName(s utils.Initial) ReaderIOEither[context.Context, error, string] { + return Of[context.Context, error]("Doe") +} + +func getGivenName(s utils.WithLastName) ReaderIOEither[context.Context, error, string] { + return Of[context.Context, error]("John") +} + +func TestBind(t *testing.T) { + + res := F.Pipe3( + Do[context.Context, error](utils.Empty), + Bind(utils.SetLastName, getLastName), + Bind(utils.SetGivenName, getGivenName), + Map[context.Context, error](utils.GetFullName), + ) + + assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe")) +} + +func TestApS(t *testing.T) { + + res := F.Pipe3( + Do[context.Context, error](utils.Empty), + ApS(utils.SetLastName, Of[context.Context, error]("Doe")), + ApS(utils.SetGivenName, Of[context.Context, error]("John")), + Map[context.Context, error](utils.GetFullName), + ) + + assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe")) +} diff --git a/v2/readerioeither/bracket.go b/v2/readerioeither/bracket.go new file mode 100644 index 0000000..cbfe672 --- /dev/null +++ b/v2/readerioeither/bracket.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[ + R, E, A, B, ANY any]( + + acquire ReaderIOEither[R, E, A], + use func(A) ReaderIOEither[R, E, B], + release func(A, ET.Either[E, B]) ReaderIOEither[R, E, ANY], +) ReaderIOEither[R, E, B] { + return G.Bracket(acquire, use, release) +} diff --git a/v2/readerioeither/doc.go b/v2/readerioeither/doc.go new file mode 100644 index 0000000..0e14f28 --- /dev/null +++ b/v2/readerioeither/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +//go:generate go run .. readerioeither --count 10 --filename gen.go diff --git a/v2/readerioeither/eq.go b/v2/readerioeither/eq.go new file mode 100644 index 0000000..2e1e2ad --- /dev/null +++ b/v2/readerioeither/eq.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// Eq implements the equals predicate for values contained in the IOEither monad +func Eq[R, E, A any](eq EQ.Eq[ET.Either[E, A]]) func(R) EQ.Eq[ReaderIOEither[R, E, A]] { + return G.Eq[ReaderIOEither[R, E, A]](eq) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[R, E, A comparable]() func(R) EQ.Eq[ReaderIOEither[R, E, A]] { + return G.FromStrictEquals[ReaderIOEither[R, E, A]]() +} diff --git a/v2/readerioeither/gen.go b/v2/readerioeither/gen.go new file mode 100644 index 0000000..dbf6b63 --- /dev/null +++ b/v2/readerioeither/gen.go @@ -0,0 +1,207 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-05-24 17:26:07.2835624 +0200 CEST m=+0.011499301 + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// From0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From0[F ~func(C) func() (R, error), C, R any](f F) func() ReaderIOEither[C, error, R] { + return G.From0[ReaderIOEither[C, error, R]](f) +} + +// Eitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize0[F ~func(C) (R, error), C, R any](f F) func() ReaderIOEither[C, error, R] { + return G.Eitherize0[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize0 converts a function with 1 parameters returning a [ReaderIOEither[C, error, R]] into a function with 0 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize0[F ~func() ReaderIOEither[C, error, R], C, R any](f F) func(C) (R, error) { + return G.Uneitherize0[ReaderIOEither[C, error, R], func(C) (R, error)](f) +} + +// From1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From1[F ~func(C, T0) func() (R, error), T0, C, R any](f F) func(T0) ReaderIOEither[C, error, R] { + return G.From1[ReaderIOEither[C, error, R]](f) +} + +// Eitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize1[F ~func(C, T0) (R, error), T0, C, R any](f F) func(T0) ReaderIOEither[C, error, R] { + return G.Eitherize1[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize1 converts a function with 2 parameters returning a [ReaderIOEither[C, error, R]] into a function with 1 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize1[F ~func(T0) ReaderIOEither[C, error, R], T0, C, R any](f F) func(C, T0) (R, error) { + return G.Uneitherize1[ReaderIOEither[C, error, R], func(C, T0) (R, error)](f) +} + +// From2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From2[F ~func(C, T0, T1) func() (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOEither[C, error, R] { + return G.From2[ReaderIOEither[C, error, R]](f) +} + +// Eitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize2[F ~func(C, T0, T1) (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOEither[C, error, R] { + return G.Eitherize2[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize2 converts a function with 3 parameters returning a [ReaderIOEither[C, error, R]] into a function with 2 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize2[F ~func(T0, T1) ReaderIOEither[C, error, R], T0, T1, C, R any](f F) func(C, T0, T1) (R, error) { + return G.Uneitherize2[ReaderIOEither[C, error, R], func(C, T0, T1) (R, error)](f) +} + +// From3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From3[F ~func(C, T0, T1, T2) func() (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOEither[C, error, R] { + return G.From3[ReaderIOEither[C, error, R]](f) +} + +// Eitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize3[F ~func(C, T0, T1, T2) (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOEither[C, error, R] { + return G.Eitherize3[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize3 converts a function with 4 parameters returning a [ReaderIOEither[C, error, R]] into a function with 3 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize3[F ~func(T0, T1, T2) ReaderIOEither[C, error, R], T0, T1, T2, C, R any](f F) func(C, T0, T1, T2) (R, error) { + return G.Uneitherize3[ReaderIOEither[C, error, R], func(C, T0, T1, T2) (R, error)](f) +} + +// From4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From4[F ~func(C, T0, T1, T2, T3) func() (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[C, error, R] { + return G.From4[ReaderIOEither[C, error, R]](f) +} + +// Eitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize4[F ~func(C, T0, T1, T2, T3) (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[C, error, R] { + return G.Eitherize4[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize4 converts a function with 5 parameters returning a [ReaderIOEither[C, error, R]] into a function with 4 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize4[F ~func(T0, T1, T2, T3) ReaderIOEither[C, error, R], T0, T1, T2, T3, C, R any](f F) func(C, T0, T1, T2, T3) (R, error) { + return G.Uneitherize4[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3) (R, error)](f) +} + +// From5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From5[F ~func(C, T0, T1, T2, T3, T4) func() (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R] { + return G.From5[ReaderIOEither[C, error, R]](f) +} + +// Eitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize5[F ~func(C, T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R] { + return G.Eitherize5[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize5 converts a function with 6 parameters returning a [ReaderIOEither[C, error, R]] into a function with 5 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize5[F ~func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, C, R any](f F) func(C, T0, T1, T2, T3, T4) (R, error) { + return G.Uneitherize5[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4) (R, error)](f) +} + +// From6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From6[F ~func(C, T0, T1, T2, T3, T4, T5) func() (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R] { + return G.From6[ReaderIOEither[C, error, R]](f) +} + +// Eitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize6[F ~func(C, T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R] { + return G.Eitherize6[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize6 converts a function with 7 parameters returning a [ReaderIOEither[C, error, R]] into a function with 6 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5) (R, error) { + return G.Uneitherize6[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5) (R, error)](f) +} + +// From7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) func() (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R] { + return G.From7[ReaderIOEither[C, error, R]](f) +} + +// Eitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R] { + return G.Eitherize7[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize7 converts a function with 8 parameters returning a [ReaderIOEither[C, error, R]] into a function with 7 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6) (R, error) { + return G.Uneitherize7[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6) (R, error)](f) +} + +// From8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R] { + return G.From8[ReaderIOEither[C, error, R]](f) +} + +// Eitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R] { + return G.Eitherize8[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize8 converts a function with 9 parameters returning a [ReaderIOEither[C, error, R]] into a function with 8 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error) { + return G.Uneitherize8[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error)](f) +} + +// From9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R] { + return G.From9[ReaderIOEither[C, error, R]](f) +} + +// Eitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R] { + return G.Eitherize9[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize9 converts a function with 10 parameters returning a [ReaderIOEither[C, error, R]] into a function with 9 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error) { + return G.Uneitherize9[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error)](f) +} + +// From10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[R]] +// The first parameter is considered to be the context [C]. +func From10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R] { + return G.From10[ReaderIOEither[C, error, R]](f) +} + +// Eitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[C, error, R]] +// The first parameter is considered to be the context [C]. +func Eitherize10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R] { + return G.Eitherize10[ReaderIOEither[C, error, R]](f) +} + +// Uneitherize10 converts a function with 11 parameters returning a [ReaderIOEither[C, error, R]] into a function with 10 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) { + return G.Uneitherize10[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error)](f) +} diff --git a/v2/readerioeither/generic/bind.go b/v2/readerioeither/generic/bind.go new file mode 100644 index 0000000..98d7177 --- /dev/null +++ b/v2/readerioeither/generic/bind.go @@ -0,0 +1,90 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GRS ~func(R) GS, GS ~func() ET.Either[E, S], R, E, S any]( + empty S, +) GRS { + return Of[GRS, GS, R, E, S](empty) +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f func(S1) GRT, +) func(GRS1) GRS2 { + return C.Bind( + Chain[GRS1, GRS2, GS1, GS2, R, E, S1, S2], + Map[GRT, GRS2, GT, GS2, R, E, T, S2], + setter, + f, + ) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], R, E, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GRS1) GRS2 { + return F.Let( + Map[GRS1, GRS2, GS1, GS2, R, E, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], R, E, S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GRS1) GRS2 { + return F.LetTo( + Map[GRS1, GRS2, GS1, GS2, R, E, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GRS1 ~func(R) GS1, GRT ~func(R) GT, GS1 ~func() ET.Either[E, S1], GT ~func() ET.Either[E, T], R, E, S1, T any]( + setter func(T) S1, +) func(GRT) GRS1 { + return C.BindTo( + Map[GRT, GRS1, GT, GS1, R, E, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GRTS1 ~func(R) GTS1, GRS1 ~func(R) GS1, GRS2 ~func(R) GS2, GRT ~func(R) GT, GTS1 ~func() ET.Either[E, func(T) S2], GS1 ~func() ET.Either[E, S1], GS2 ~func() ET.Either[E, S2], GT ~func() ET.Either[E, T], R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa GRT, +) func(GRS1) GRS2 { + return A.ApS( + Ap[GRT, GRS2, GRTS1, GT, GS2, GTS1, R, E, T, S2], + Map[GRS1, GRTS1, GS1, GTS1, R, E, S1, func(T) S2], + setter, + fa, + ) +} diff --git a/v2/readerioeither/generic/bracket.go b/v2/readerioeither/generic/bracket.go new file mode 100644 index 0000000..0c32999 --- /dev/null +++ b/v2/readerioeither/generic/bracket.go @@ -0,0 +1,51 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/internal/bracket" + I "github.com/IBM/fp-go/v2/readerio/generic" +) + +// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of +// whether the body action returns and error or not. +func Bracket[ + GA ~func(R) TA, + GB ~func(R) TB, + GANY ~func(R) TANY, + + TA ~func() ET.Either[E, A], + TB ~func() ET.Either[E, B], + TANY ~func() ET.Either[E, ANY], + + R, E, A, B, ANY any]( + + acquire GA, + use func(A) GB, + release func(A, ET.Either[E, B]) GANY, +) GB { + return G.Bracket[GA, GB, GANY, ET.Either[E, B], A, B]( + I.Of[GB, TB, R, ET.Either[E, B]], + MonadChain[GA, GB, TA, TB, R, E, A, B], + I.MonadChain[GB, GB, TB, TB, R, ET.Either[E, B], ET.Either[E, B]], + MonadChain[GANY, GB, TANY, TB, R, E, ANY, B], + + acquire, + use, + release, + ) +} diff --git a/v2/readerioeither/generic/eq.go b/v2/readerioeither/generic/eq.go new file mode 100644 index 0000000..28101cc --- /dev/null +++ b/v2/readerioeither/generic/eq.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// Eq implements the equals predicate for values contained in the IOEither monad +func Eq[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](eq EQ.Eq[ET.Either[E, A]]) func(R) EQ.Eq[GEA] { + return G.Eq[GEA](eq) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R any, E, A comparable]() func(R) EQ.Eq[GEA] { + return Eq[GEA](ET.FromStrictEquals[E, A]()) +} diff --git a/v2/readerioeither/generic/gen.go b/v2/readerioeither/generic/gen.go new file mode 100644 index 0000000..050394b --- /dev/null +++ b/v2/readerioeither/generic/gen.go @@ -0,0 +1,295 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-05-24 17:26:07.2835624 +0200 CEST m=+0.011499301 +package generic + +import ( + E "github.com/IBM/fp-go/v2/either" + RD "github.com/IBM/fp-go/v2/reader/generic" +) + +// From0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From0[GRA ~func(C) GIOA, F ~func(C) func() (R, error), GIOA ~func() E.Either[error, R], C, R any](f F) func() GRA { + return RD.From0[GRA](func(r C) GIOA { + return E.Eitherize0(f(r)) + }) +} + +// Eitherize0 converts a function with 0 parameters returning a tuple into a function with 0 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize0[GRA ~func(C) GIOA, F ~func(C) (R, error), GIOA ~func() E.Either[error, R], C, R any](f F) func() GRA { + return From0[GRA](func(r C) func() (R, error) { + return func() (R, error) { + return f(r) + } + }) +} + +// Uneitherize0 converts a function with 0 parameters returning a [GRA] into a function with 0 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize0[GRA ~func(C) GIOA, F ~func(C) (R, error), GIOA ~func() E.Either[error, R], C, R any](f func() GRA) F { + return func(c C) (R, error) { + return E.UnwrapError(f()(c)()) + } +} + +// From1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From1[GRA ~func(C) GIOA, F ~func(C, T0) func() (R, error), GIOA ~func() E.Either[error, R], T0, C, R any](f F) func(T0) GRA { + return RD.From1[GRA](func(r C, t0 T0) GIOA { + return E.Eitherize0(f(r, t0)) + }) +} + +// Eitherize1 converts a function with 1 parameters returning a tuple into a function with 1 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize1[GRA ~func(C) GIOA, F ~func(C, T0) (R, error), GIOA ~func() E.Either[error, R], T0, C, R any](f F) func(T0) GRA { + return From1[GRA](func(r C, t0 T0) func() (R, error) { + return func() (R, error) { + return f(r, t0) + } + }) +} + +// Uneitherize1 converts a function with 1 parameters returning a [GRA] into a function with 1 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize1[GRA ~func(C) GIOA, F ~func(C, T0) (R, error), GIOA ~func() E.Either[error, R], T0, C, R any](f func(T0) GRA) F { + return func(c C, t0 T0) (R, error) { + return E.UnwrapError(f(t0)(c)()) + } +} + +// From2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From2[GRA ~func(C) GIOA, F ~func(C, T0, T1) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, C, R any](f F) func(T0, T1) GRA { + return RD.From2[GRA](func(r C, t0 T0, t1 T1) GIOA { + return E.Eitherize0(f(r, t0, t1)) + }) +} + +// Eitherize2 converts a function with 2 parameters returning a tuple into a function with 2 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize2[GRA ~func(C) GIOA, F ~func(C, T0, T1) (R, error), GIOA ~func() E.Either[error, R], T0, T1, C, R any](f F) func(T0, T1) GRA { + return From2[GRA](func(r C, t0 T0, t1 T1) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1) + } + }) +} + +// Uneitherize2 converts a function with 2 parameters returning a [GRA] into a function with 2 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize2[GRA ~func(C) GIOA, F ~func(C, T0, T1) (R, error), GIOA ~func() E.Either[error, R], T0, T1, C, R any](f func(T0, T1) GRA) F { + return func(c C, t0 T0, t1 T1) (R, error) { + return E.UnwrapError(f(t0, t1)(c)()) + } +} + +// From3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From3[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, C, R any](f F) func(T0, T1, T2) GRA { + return RD.From3[GRA](func(r C, t0 T0, t1 T1, t2 T2) GIOA { + return E.Eitherize0(f(r, t0, t1, t2)) + }) +} + +// Eitherize3 converts a function with 3 parameters returning a tuple into a function with 3 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize3[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, C, R any](f F) func(T0, T1, T2) GRA { + return From3[GRA](func(r C, t0 T0, t1 T1, t2 T2) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1, t2) + } + }) +} + +// Uneitherize3 converts a function with 3 parameters returning a [GRA] into a function with 3 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize3[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, C, R any](f func(T0, T1, T2) GRA) F { + return func(c C, t0 T0, t1 T1, t2 T2) (R, error) { + return E.UnwrapError(f(t0, t1, t2)(c)()) + } +} + +// From4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From4[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) GRA { + return RD.From4[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3) GIOA { + return E.Eitherize0(f(r, t0, t1, t2, t3)) + }) +} + +// Eitherize4 converts a function with 4 parameters returning a tuple into a function with 4 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize4[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) GRA { + return From4[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1, t2, t3) + } + }) +} + +// Uneitherize4 converts a function with 4 parameters returning a [GRA] into a function with 4 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize4[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, C, R any](f func(T0, T1, T2, T3) GRA) F { + return func(c C, t0 T0, t1 T1, t2 T2, t3 T3) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3)(c)()) + } +} + +// From5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From5[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) GRA { + return RD.From5[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) GIOA { + return E.Eitherize0(f(r, t0, t1, t2, t3, t4)) + }) +} + +// Eitherize5 converts a function with 5 parameters returning a tuple into a function with 5 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize5[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) GRA { + return From5[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1, t2, t3, t4) + } + }) +} + +// Uneitherize5 converts a function with 5 parameters returning a [GRA] into a function with 5 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize5[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, C, R any](f func(T0, T1, T2, T3, T4) GRA) F { + return func(c C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4)(c)()) + } +} + +// From6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From6[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) GRA { + return RD.From6[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) GIOA { + return E.Eitherize0(f(r, t0, t1, t2, t3, t4, t5)) + }) +} + +// Eitherize6 converts a function with 6 parameters returning a tuple into a function with 6 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize6[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) GRA { + return From6[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1, t2, t3, t4, t5) + } + }) +} + +// Uneitherize6 converts a function with 6 parameters returning a [GRA] into a function with 6 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize6[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, C, R any](f func(T0, T1, T2, T3, T4, T5) GRA) F { + return func(c C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5)(c)()) + } +} + +// From7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From7[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) GRA { + return RD.From7[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) GIOA { + return E.Eitherize0(f(r, t0, t1, t2, t3, t4, t5, t6)) + }) +} + +// Eitherize7 converts a function with 7 parameters returning a tuple into a function with 7 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize7[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) GRA { + return From7[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1, t2, t3, t4, t5, t6) + } + }) +} + +// Uneitherize7 converts a function with 7 parameters returning a [GRA] into a function with 7 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize7[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, C, R any](f func(T0, T1, T2, T3, T4, T5, T6) GRA) F { + return func(c C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6)(c)()) + } +} + +// From8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From8[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) GRA { + return RD.From8[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) GIOA { + return E.Eitherize0(f(r, t0, t1, t2, t3, t4, t5, t6, t7)) + }) +} + +// Eitherize8 converts a function with 8 parameters returning a tuple into a function with 8 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize8[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) GRA { + return From8[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1, t2, t3, t4, t5, t6, t7) + } + }) +} + +// Uneitherize8 converts a function with 8 parameters returning a [GRA] into a function with 8 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize8[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7) GRA) F { + return func(c C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7)(c)()) + } +} + +// From9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From9[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) GRA { + return RD.From9[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) GIOA { + return E.Eitherize0(f(r, t0, t1, t2, t3, t4, t5, t6, t7, t8)) + }) +} + +// Eitherize9 converts a function with 9 parameters returning a tuple into a function with 9 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize9[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) GRA { + return From9[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1, t2, t3, t4, t5, t6, t7, t8) + } + }) +} + +// Uneitherize9 converts a function with 9 parameters returning a [GRA] into a function with 9 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize9[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8) GRA) F { + return func(c C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8)(c)()) + } +} + +// From10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [GRA] +// The first parameter is considerd to be the context [C]. +func From10[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) func() (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) GRA { + return RD.From10[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) GIOA { + return E.Eitherize0(f(r, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)) + }) +} + +// Eitherize10 converts a function with 10 parameters returning a tuple into a function with 10 parameters returning a [GRA] +// The first parameter is considered to be the context [C]. +func Eitherize10[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) GRA { + return From10[GRA](func(r C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) func() (R, error) { + return func() (R, error) { + return f(r, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) + } + }) +} + +// Uneitherize10 converts a function with 10 parameters returning a [GRA] into a function with 10 parameters returning a tuple. +// The first parameter is considered to be the context [C]. +func Uneitherize10[GRA ~func(C) GIOA, F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), GIOA ~func() E.Either[error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) GRA) F { + return func(c C, t0 T0, t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) (R, error) { + return E.UnwrapError(f(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9)(c)()) + } +} diff --git a/v2/readerioeither/generic/monad.go b/v2/readerioeither/generic/monad.go new file mode 100644 index 0000000..32a844e --- /dev/null +++ b/v2/readerioeither/generic/monad.go @@ -0,0 +1,68 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" +) + +type readerIOEitherPointed[R, E, A any, GRA ~func(R) GIOA, GIOA ~func() ET.Either[E, A]] struct{} + +type readerIOEitherMonad[R, E, A, B any, GRA ~func(R) GIOA, GRB ~func(R) GIOB, GRAB ~func(R) GIOAB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GIOAB ~func() ET.Either[E, func(A) B]] struct{} + +type readerIOEitherFunctor[R, E, A, B any, GRA ~func(R) GIOA, GRB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B]] struct{} + +func (o *readerIOEitherPointed[R, E, A, GRA, GIOA]) Of(a A) GRA { + return Of[GRA, GIOA, R, E, A](a) +} + +func (o *readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]) Of(a A) GRA { + return Of[GRA, GIOA, R, E, A](a) +} + +func (o *readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]) Map(f func(A) B) func(GRA) GRB { + return Map[GRA, GRB, GIOA, GIOB, R, E, A, B](f) +} + +func (o *readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]) Chain(f func(A) GRB) func(GRA) GRB { + return Chain[GRA, GRB, GIOA, GIOB, R, E, A, B](f) +} + +func (o *readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]) Ap(fa GRA) func(GRAB) GRB { + return Ap[GRA, GRB, GRAB, GIOA, GIOB, GIOAB, R, E, A, B](fa) +} + +func (o *readerIOEitherFunctor[R, E, A, B, GRA, GRB, GIOA, GIOB]) Map(f func(A) B) func(GRA) GRB { + return Map[GRA, GRB, GIOA, GIOB, R, E, A, B](f) +} + +// Pointed implements the pointed operations for [ReaderIOEither] +func Pointed[R, E, A any, GRA ~func(R) GIOA, GIOA ~func() ET.Either[E, A]]() pointed.Pointed[A, GRA] { + return &readerIOEitherPointed[R, E, A, GRA, GIOA]{} +} + +// Functor implements the monadic operations for [ReaderIOEither] +func Functor[R, E, A, B any, GRA ~func(R) GIOA, GRB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B]]() functor.Functor[A, B, GRA, GRB] { + return &readerIOEitherFunctor[R, E, A, B, GRA, GRB, GIOA, GIOB]{} +} + +// Monad implements the monadic operations for [ReaderIOEither] +func Monad[R, E, A, B any, GRA ~func(R) GIOA, GRB ~func(R) GIOB, GRAB ~func(R) GIOAB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GIOAB ~func() ET.Either[E, func(A) B]]() monad.Monad[A, B, GRA, GRB, GRAB] { + return &readerIOEitherMonad[R, E, A, B, GRA, GRB, GRAB, GIOA, GIOB, GIOAB]{} +} diff --git a/v2/readerioeither/generic/reader.go b/v2/readerioeither/generic/reader.go new file mode 100644 index 0000000..830ba9c --- /dev/null +++ b/v2/readerioeither/generic/reader.go @@ -0,0 +1,463 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + "github.com/IBM/fp-go/v2/internal/eithert" + FE "github.com/IBM/fp-go/v2/internal/fromeither" + FIO "github.com/IBM/fp-go/v2/internal/fromio" + FIOE "github.com/IBM/fp-go/v2/internal/fromioeither" + FR "github.com/IBM/fp-go/v2/internal/fromreader" + FC "github.com/IBM/fp-go/v2/internal/functor" + IOE "github.com/IBM/fp-go/v2/ioeither/generic" + O "github.com/IBM/fp-go/v2/option" + RD "github.com/IBM/fp-go/v2/reader/generic" + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// MakeReader constructs an instance of a reader +func MakeReader[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](f func(R) GIOA) GEA { + return f +} + +func MonadAlt[LAZY ~func() GEA, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](first GEA, second LAZY) GEA { + return eithert.MonadAlt( + G.Of[GEA], + G.MonadChain[GEA, GEA], + + first, + second, + ) +} + +func Alt[LAZY ~func() GEA, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](second LAZY) func(GEA) GEA { + return F.Bind2nd(MonadAlt[LAZY], second) +} + +func MonadMap[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](fa GEA, f func(A) B) GEB { + return eithert.MonadMap(G.MonadMap[GEA, GEB, GIOA, GIOB, R, ET.Either[E, A], ET.Either[E, B]], fa, f) +} + +func Map[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](f func(A) B) func(GEA) GEB { + return F.Bind2nd(MonadMap[GEA, GEB, GIOA, GIOB, R, E, A, B], f) +} + +func MonadMapTo[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](fa GEA, b B) GEB { + return MonadMap[GEA, GEB](fa, F.Constant1[A](b)) +} + +func MapTo[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](b B) func(GEA) GEB { + return Map[GEA, GEB](F.Constant1[A](b)) +} + +func MonadChain[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](fa GEA, f func(A) GEB) GEB { + return eithert.MonadChain( + G.MonadChain[GEA, GEB, GIOA, GIOB, R, ET.Either[E, A], ET.Either[E, B]], + G.Of[GEB, GIOB, R, ET.Either[E, B]], + fa, + f) +} + +func Chain[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](f func(A) GEB) func(fa GEA) GEB { + return F.Bind2nd(MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], f) +} + +func MonadChainFirst[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](fa GEA, f func(A) GEB) GEA { + return C.MonadChainFirst( + MonadChain[GEA, GEA, GIOA, GIOA, R, E, A, A], + MonadMap[GEB, GEA, GIOB, GIOA, R, E, B, A], + fa, + f) +} + +func ChainFirst[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](f func(A) GEB) func(fa GEA) GEA { + return F.Bind2nd(MonadChainFirst[GEA, GEB, GIOA, GIOB, R, E, A, B], f) +} + +func MonadChainEitherK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](ma GEA, f func(A) ET.Either[E, B]) GEB { + return FE.MonadChainEitherK( + MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], + FromEither[GEB, GIOB, R, E, B], + ma, + f, + ) +} + +func ChainEitherK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](f func(A) ET.Either[E, B]) func(ma GEA) GEB { + return F.Bind2nd(MonadChainEitherK[GEA, GEB, GIOA, GIOB, R, E, A, B], f) +} + +func MonadChainFirstEitherK[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A, B any](ma GEA, f func(A) ET.Either[E, B]) GEA { + return FE.MonadChainFirstEitherK( + MonadChain[GEA, GEA, GIOA, GIOA, R, E, A, A], + MonadMap[func(R) func() ET.Either[E, B], GEA, func() ET.Either[E, B], GIOA, R, E, B, A], + FromEither[func(R) func() ET.Either[E, B], func() ET.Either[E, B], R, E, B], + ma, + f, + ) +} + +func ChainFirstEitherK[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A, B any](f func(A) ET.Either[E, B]) func(ma GEA) GEA { + return F.Bind2nd(MonadChainFirstEitherK[GEA, GIOA, R, E, A, B], f) +} + +func MonadChainFirstIOK[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GIO ~func() B, R, E, A, B any](ma GEA, f func(A) GIO) GEA { + return FIO.MonadChainFirstIOK( + MonadChain[GEA, GEA, GIOA, GIOA, R, E, A, A], + MonadMap[func(R) func() ET.Either[E, B], GEA, func() ET.Either[E, B], GIOA, R, E, B, A], + FromIO[func(R) func() ET.Either[E, B], func() ET.Either[E, B], GIO, R, E, B], + ma, + f, + ) +} + +func ChainFirstIOK[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GIO ~func() B, R, E, A, B any](f func(A) GIO) func(GEA) GEA { + return F.Bind2nd(MonadChainFirstIOK[GEA, GIOA, GIO, R, E, A, B], f) +} + +func MonadChainReaderK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GB ~func(R) B, R, E, A, B any](ma GEA, f func(A) GB) GEB { + return FR.MonadChainReaderK( + MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], + FromReader[GB, GEB, GIOB, R, E, B], + ma, + f, + ) +} + +func ChainReaderK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GB ~func(R) B, R, E, A, B any](f func(A) GB) func(GEA) GEB { + return FR.ChainReaderK( + MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], + FromReader[GB, GEB, GIOB, R, E, B], + f, + ) +} + +func MonadChainReaderIOK[GEA ~func(R) GIOEA, GEB ~func(R) GIOEB, GIOEA ~func() ET.Either[E, A], GIOEB ~func() ET.Either[E, B], GIOB ~func() B, GB ~func(R) GIOB, R, E, A, B any](ma GEA, f func(A) GB) GEB { + return FR.MonadChainReaderK( + MonadChain[GEA, GEB, GIOEA, GIOEB, R, E, A, B], + RightReaderIO[GEB, GIOEB, GB, GIOB, R, E, B], + ma, + f, + ) +} + +func ChainReaderIOK[GEA ~func(R) GIOEA, GEB ~func(R) GIOEB, GIOEA ~func() ET.Either[E, A], GIOEB ~func() ET.Either[E, B], GIOB ~func() B, GB ~func(R) GIOB, R, E, A, B any](f func(A) GB) func(GEA) GEB { + return FR.ChainReaderK( + MonadChain[GEA, GEB, GIOEA, GIOEB, R, E, A, B], + RightReaderIO[GEB, GIOEB, GB, GIOB, R, E, B], + f, + ) +} + +func MonadChainIOEitherK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](ma GEA, f func(A) GIOB) GEB { + return FIOE.MonadChainIOEitherK( + MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], + FromIOEither[GEB, GIOB, R, E, B], + ma, + f, + ) +} + +func ChainIOEitherK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](f func(A) GIOB) func(GEA) GEB { + return F.Bind2nd(MonadChainIOEitherK[GEA, GEB, GIOA, GIOB, R, E, A, B], f) +} + +func MonadChainIOK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GIO ~func() B, R, E, A, B any](ma GEA, f func(A) GIO) GEB { + return FIO.MonadChainIOK( + MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], + FromIO[GEB, GIOB, GIO, R, E, B], + ma, + f, + ) +} + +func ChainIOK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], GIO ~func() B, R, E, A, B any](f func(A) GIO) func(GEA) GEB { + return F.Bind2nd(MonadChainIOK[GEA, GEB, GIOA, GIOB, GIO, R, E, A, B], f) +} + +func ChainOptionK[GEA ~func(R) GIOA, GEB ~func(R) GIOB, GIOA ~func() ET.Either[E, A], GIOB ~func() ET.Either[E, B], R, E, A, B any](onNone func() E) func(func(A) O.Option[B]) func(GEA) GEB { + return FE.ChainOptionK(MonadChain[GEA, GEB, GIOA, GIOB, R, E, A, B], FromEither[GEB, GIOB, R, E, B], onNone) +} + +func MonadAp[ + GEA ~func(R) GIOA, + GEB ~func(R) GIOB, + GEFAB ~func(R) GIOFAB, + GIOA ~func() ET.Either[E, A], + GIOB ~func() ET.Either[E, B], + GIOFAB ~func() ET.Either[E, func(A) B], + R, E, A, B any](fab GEFAB, fa GEA) GEB { + + return eithert.MonadAp( + G.MonadAp[GEA, GEB, func(R) func() func(ET.Either[E, A]) ET.Either[E, B], GIOA, GIOB, func() func(ET.Either[E, A]) ET.Either[E, B], R, ET.Either[E, A], ET.Either[E, B]], + G.MonadMap[GEFAB, func(R) func() func(ET.Either[E, A]) ET.Either[E, B], GIOFAB, func() func(ET.Either[E, A]) ET.Either[E, B], R, ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + fab, + fa, + ) +} + +func Ap[ + GEA ~func(R) GIOA, + GEB ~func(R) GIOB, + GEFAB ~func(R) GIOFAB, + GIOA ~func() ET.Either[E, A], + GIOB ~func() ET.Either[E, B], + GIOFAB ~func() ET.Either[E, func(A) B], + R, E, A, B any](fa GEA) func(fab GEFAB) GEB { + return F.Bind2nd(MonadAp[GEA, GEB, GEFAB, GIOA, GIOB, GIOFAB, R, E, A, B], fa) +} + +func MonadApSeq[ + GEA ~func(R) GIOA, + GEB ~func(R) GIOB, + GEFAB ~func(R) GIOFAB, + GIOA ~func() ET.Either[E, A], + GIOB ~func() ET.Either[E, B], + GIOFAB ~func() ET.Either[E, func(A) B], + R, E, A, B any](fab GEFAB, fa GEA) GEB { + + return eithert.MonadAp( + G.MonadApSeq[GEA, GEB, func(R) func() func(ET.Either[E, A]) ET.Either[E, B], GIOA, GIOB, func() func(ET.Either[E, A]) ET.Either[E, B], R, ET.Either[E, A], ET.Either[E, B]], + G.MonadMap[GEFAB, func(R) func() func(ET.Either[E, A]) ET.Either[E, B], GIOFAB, func() func(ET.Either[E, A]) ET.Either[E, B], R, ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + fab, + fa, + ) +} + +func ApSeq[ + GEA ~func(R) GIOA, + GEB ~func(R) GIOB, + GEFAB ~func(R) GIOFAB, + GIOA ~func() ET.Either[E, A], + GIOB ~func() ET.Either[E, B], + GIOFAB ~func() ET.Either[E, func(A) B], + R, E, A, B any](fa GEA) func(fab GEFAB) GEB { + return F.Bind2nd(MonadApSeq[GEA, GEB, GEFAB, GIOA, GIOB, GIOFAB, R, E, A, B], fa) +} + +func MonadApPar[ + GEA ~func(R) GIOA, + GEB ~func(R) GIOB, + GEFAB ~func(R) GIOFAB, + GIOA ~func() ET.Either[E, A], + GIOB ~func() ET.Either[E, B], + GIOFAB ~func() ET.Either[E, func(A) B], + R, E, A, B any](fab GEFAB, fa GEA) GEB { + + return eithert.MonadAp( + G.MonadApPar[GEA, GEB, func(R) func() func(ET.Either[E, A]) ET.Either[E, B], GIOA, GIOB, func() func(ET.Either[E, A]) ET.Either[E, B], R, ET.Either[E, A], ET.Either[E, B]], + G.MonadMap[GEFAB, func(R) func() func(ET.Either[E, A]) ET.Either[E, B], GIOFAB, func() func(ET.Either[E, A]) ET.Either[E, B], R, ET.Either[E, func(A) B], func(ET.Either[E, A]) ET.Either[E, B]], + fab, + fa, + ) +} + +func ApPar[ + GEA ~func(R) GIOA, + GEB ~func(R) GIOB, + GEFAB ~func(R) GIOFAB, + GIOA ~func() ET.Either[E, A], + GIOB ~func() ET.Either[E, B], + GIOFAB ~func() ET.Either[E, func(A) B], + R, E, A, B any](fa GEA) func(fab GEFAB) GEB { + return F.Bind2nd(MonadApPar[GEA, GEB, GEFAB, GIOA, GIOB, GIOFAB, R, E, A, B], fa) +} + +func Right[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](a A) GEA { + return eithert.Right(G.Of[GEA, GIOA, R, ET.Either[E, A]], a) +} + +func Left[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](e E) GEA { + return eithert.Left(G.Of[GEA, GIOA, R, ET.Either[E, A]], e) +} + +func ThrowError[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](e E) GEA { + return Left[GEA](e) +} + +// Of returns a Reader with a fixed value +func Of[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](a A) GEA { + return Right[GEA](a) +} + +func Flatten[GEA ~func(R) GIOA, GGEA ~func(R) GIOEA, GIOA ~func() ET.Either[E, A], GIOEA ~func() ET.Either[E, GEA], R, E, A any](mma GGEA) GEA { + return MonadChain(mma, F.Identity[GEA]) +} + +func FromIOEither[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](t GIOA) GEA { + return RD.Of[GEA](t) +} + +func FromEither[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](t ET.Either[E, A]) GEA { + return G.Of[GEA](t) +} + +func RightReader[GA ~func(R) A, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](ma GA) GEA { + return F.Flow2(ma, IOE.Right[GIOA, E, A]) +} + +func LeftReader[GE ~func(R) E, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](ma GE) GEA { + return F.Flow2(ma, IOE.Left[GIOA, E, A]) +} + +func FromReader[GA ~func(R) A, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](ma GA) GEA { + return RightReader[GA, GEA](ma) +} + +func MonadFromReaderIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GRIO ~func(R) GIO, GIO ~func() A, R, E, A any](a A, f func(A) GRIO) GEA { + return F.Pipe2( + a, + f, + RightReaderIO[GEA, GIOA, GRIO, GIO, R, E, A], + ) +} + +func FromReaderIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GRIO ~func(R) GIO, GIO ~func() A, R, E, A any](f func(A) GRIO) func(A) GEA { + return F.Bind2nd(MonadFromReaderIO[GEA, GIOA, GRIO, GIO, R, E, A], f) +} + +func RightReaderIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GRIO ~func(R) GIO, GIO ~func() A, R, E, A any](ma GRIO) GEA { + return eithert.RightF( + G.MonadMap[GRIO, GEA, GIO, GIOA, R, A, ET.Either[E, A]], + ma, + ) +} + +func LeftReaderIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GRIO ~func(R) GIO, GIO ~func() E, R, E, A any](me GRIO) GEA { + return eithert.LeftF( + G.MonadMap[GRIO, GEA, GIO, GIOA, R, E, ET.Either[E, A]], + me, + ) +} + +func RightIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GR ~func() A, R, E, A any](ma GR) GEA { + return F.Pipe2(ma, IOE.RightIO[GIOA, GR, E, A], FromIOEither[GEA, GIOA, R, E, A]) +} + +func LeftIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GR ~func() E, R, E, A any](ma GR) GEA { + return F.Pipe2(ma, IOE.LeftIO[GIOA, GR, E, A], FromIOEither[GEA, GIOA, R, E, A]) +} + +func FromIO[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], GR ~func() A, R, E, A any](ma GR) GEA { + return RightIO[GEA](ma) +} + +func FromReaderEither[GA ~func(R) ET.Either[E, A], GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](ma GA) GEA { + return F.Flow2(ma, IOE.FromEither[GIOA, E, A]) +} + +func Ask[GER ~func(R) GIOR, GIOR ~func() ET.Either[E, R], R, E any]() GER { + return FR.Ask(FromReader[func(R) R, GER, GIOR, R, E, R])() +} + +func Asks[GA ~func(R) A, GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](r GA) GEA { + return FR.Asks(FromReader[GA, GEA, GIOA, R, E, A])(r) +} + +func FromOption[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](onNone func() E) func(O.Option[A]) GEA { + return FE.FromOption(FromEither[GEA, GIOA, R, E, A], onNone) +} + +func FromPredicate[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](pred func(A) bool, onFalse func(A) E) func(A) GEA { + return FE.FromPredicate(FromEither[GEA, GIOA, R, E, A], pred, onFalse) +} + +func Fold[GB ~func(R) GIOB, GEA ~func(R) GIOA, GIOB ~func() B, GIOA ~func() ET.Either[E, A], R, E, A, B any](onLeft func(E) GB, onRight func(A) GB) func(GEA) GB { + return eithert.MatchE(G.MonadChain[GEA, GB, GIOA, GIOB, R, ET.Either[E, A], B], onLeft, onRight) +} + +func GetOrElse[GA ~func(R) GIOB, GEA ~func(R) GIOA, GIOB ~func() A, GIOA ~func() ET.Either[E, A], R, E, A any](onLeft func(E) GA) func(GEA) GA { + return eithert.GetOrElse(G.MonadChain[GEA, GA, GIOA, GIOB, R, ET.Either[E, A], A], G.Of[GA, GIOB, R, A], onLeft) +} + +func OrElse[GEA1 ~func(R) GIOA1, GEA2 ~func(R) GIOA2, GIOA1 ~func() ET.Either[E1, A], GIOA2 ~func() ET.Either[E2, A], R, E1, A, E2 any](onLeft func(E1) GEA2) func(GEA1) GEA2 { + return eithert.OrElse(G.MonadChain[GEA1, GEA2, GIOA1, GIOA2, R, ET.Either[E1, A], ET.Either[E2, A]], G.Of[GEA2, GIOA2, R, ET.Either[E2, A]], onLeft) +} + +func OrLeft[GEA1 ~func(R) GIOA1, GE2 ~func(R) GIOE2, GEA2 ~func(R) GIOA2, GIOA1 ~func() ET.Either[E1, A], GIOE2 ~func() E2, GIOA2 ~func() ET.Either[E2, A], E1, R, E2, A any](onLeft func(E1) GE2) func(GEA1) GEA2 { + return eithert.OrLeft( + G.MonadChain[GEA1, GEA2, GIOA1, GIOA2, R, ET.Either[E1, A], ET.Either[E2, A]], + G.MonadMap[GE2, GEA2, GIOE2, GIOA2, R, E2, ET.Either[E2, A]], + G.Of[GEA2, GIOA2, R, ET.Either[E2, A]], + onLeft, + ) +} + +func MonadBiMap[GA ~func(R) GE1A, GB ~func(R) GE2B, GE1A ~func() ET.Either[E1, A], GE2B ~func() ET.Either[E2, B], R, E1, E2, A, B any](fa GA, f func(E1) E2, g func(A) B) GB { + return eithert.MonadBiMap(G.MonadMap[GA, GB, GE1A, GE2B, R, ET.Either[E1, A], ET.Either[E2, B]], fa, f, g) +} + +// BiMap maps a pair of functions over the two type arguments of the bifunctor. +func BiMap[GA ~func(R) GE1A, GB ~func(R) GE2B, GE1A ~func() ET.Either[E1, A], GE2B ~func() ET.Either[E2, B], R, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(GA) GB { + return eithert.BiMap(G.Map[GA, GB, GE1A, GE2B, R, ET.Either[E1, A], ET.Either[E2, B]], f, g) +} + +// Swap changes the order of type parameters +func Swap[GREA ~func(R) GEA, GRAE ~func(R) GAE, GEA ~func() ET.Either[E, A], GAE ~func() ET.Either[A, E], R, E, A any](val GREA) GRAE { + return RD.MonadMap[GREA, GRAE, R, GEA, GAE](val, IOE.Swap[GEA, GAE]) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[GEA ~func(R) GA, GA ~func() ET.Either[E, A], R, E, A any](gen func() GEA) GEA { + return G.Defer[GEA](gen) +} + +// TryCatch wraps a reader returning a tuple as an error into ReaderIOEither +func TryCatch[GEA ~func(R) GA, GA ~func() ET.Either[E, A], R, E, A any](f func(R) func() (A, error), onThrow func(error) E) GEA { + return func(r R) GA { + return IOE.TryCatch[GA](f(r), onThrow) + } +} + +// Memoize computes the value of the provided monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[ + GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](rdr GEA) GEA { + return G.Memoize[GEA](rdr) +} + +func MonadFlap[GREAB ~func(R) GEAB, GREB ~func(R) GEB, GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], R, E, B, A any](fab GREAB, a A) GREB { + return FC.MonadFlap(MonadMap[GREAB, GREB], fab, a) +} + +func Flap[GREAB ~func(R) GEAB, GREB ~func(R) GEB, GEAB ~func() ET.Either[E, func(A) B], GEB ~func() ET.Either[E, B], R, E, B, A any](a A) func(GREAB) GREB { + return FC.Flap(Map[GREAB, GREB], a) +} + +func MonadMapLeft[GREA1 ~func(R) GEA1, GREA2 ~func(R) GEA2, GEA1 ~func() ET.Either[E1, A], GEA2 ~func() ET.Either[E2, A], R, E1, E2, A any](fa GREA1, f func(E1) E2) GREA2 { + return eithert.MonadMapLeft(G.MonadMap[GREA1, GREA2], fa, f) +} + +// MapLeft applies a mapping function to the error channel +func MapLeft[GREA1 ~func(R) GEA1, GREA2 ~func(R) GEA2, GEA1 ~func() ET.Either[E1, A], GEA2 ~func() ET.Either[E2, A], R, E1, E2, A any](f func(E1) E2) func(GREA1) GREA2 { + return F.Bind2nd(MonadMapLeft[GREA1, GREA2], f) +} + +// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s +// `contramap`). +func Local[ + GEA1 ~func(R1) GIOA, + GEA2 ~func(R2) GIOA, + + GIOA ~func() ET.Either[E, A], + R1, R2, E, A any, +](f func(R2) R1) func(GEA1) GEA2 { + return RD.Local[GEA1, GEA2](f) +} diff --git a/v2/readerioeither/generic/resource.go b/v2/readerioeither/generic/resource.go new file mode 100644 index 0000000..fa0f8bd --- /dev/null +++ b/v2/readerioeither/generic/resource.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + IOE "github.com/IBM/fp-go/v2/ioeither/generic" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[ + GEA ~func(L) TEA, + GER ~func(L) TER, + GEANY ~func(L) TEANY, + + TEA ~func() ET.Either[E, A], + TER ~func() ET.Either[E, R], + TEANY ~func() ET.Either[E, ANY], + + L, E, R, A, ANY any](onCreate GER, onRelease func(R) GEANY) func(func(R) GEA) GEA { + + return func(f func(R) GEA) GEA { + return func(l L) TEA { + // dispatch to the generic implementation + return IOE.WithResource[TEA]( + onCreate(l), + func(r R) TEANY { + return onRelease(r)(l) + }, + )(func(r R) TEA { + return f(r)(l) + }) + } + } +} diff --git a/v2/readerioeither/generic/sequence.go b/v2/readerioeither/generic/sequence.go new file mode 100644 index 0000000..4b5cb50 --- /dev/null +++ b/v2/readerioeither/generic/sequence.go @@ -0,0 +1,94 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/internal/apply" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[ + GA ~func(E) GIOA, + GTA ~func(E) GIOTA, + GIOA ~func() ET.Either[L, A], + GIOTA ~func() ET.Either[L, T.Tuple1[A]], + E, L, A any](a GA) GTA { + return apply.SequenceT1( + Map[GA, GTA, GIOA, GIOTA, E, L, A, T.Tuple1[A]], + + a, + ) +} + +func SequenceT2[ + GA ~func(E) GIOA, + GB ~func(E) GIOB, + GTAB ~func(E) GIOTAB, + GIOA ~func() ET.Either[L, A], + GIOB ~func() ET.Either[L, B], + GIOTAB ~func() ET.Either[L, T.Tuple2[A, B]], + E, L, A, B any](a GA, b GB) GTAB { + return apply.SequenceT2( + Map[GA, func(E) func() ET.Either[L, func(B) T.Tuple2[A, B]], GIOA, func() ET.Either[L, func(B) T.Tuple2[A, B]], E, L, A, func(B) T.Tuple2[A, B]], + Ap[GB, GTAB, func(E) func() ET.Either[L, func(B) T.Tuple2[A, B]], GIOB, GIOTAB, func() ET.Either[L, func(B) T.Tuple2[A, B]], E, L, B, T.Tuple2[A, B]], + + a, b, + ) +} + +func SequenceT3[ + GA ~func(E) GIOA, + GB ~func(E) GIOB, + GC ~func(E) GIOC, + GTABC ~func(E) GIOTABC, + GIOA ~func() ET.Either[L, A], + GIOB ~func() ET.Either[L, B], + GIOC ~func() ET.Either[L, C], + GIOTABC ~func() ET.Either[L, T.Tuple3[A, B, C]], + E, L, A, B, C any](a GA, b GB, c GC) GTABC { + return apply.SequenceT3( + Map[GA, func(E) func() ET.Either[L, func(B) func(C) T.Tuple3[A, B, C]], GIOA, func() ET.Either[L, func(B) func(C) T.Tuple3[A, B, C]], E, L, A, func(B) func(C) T.Tuple3[A, B, C]], + Ap[GB, func(E) func() ET.Either[L, func(C) T.Tuple3[A, B, C]], func(E) func() ET.Either[L, func(B) func(C) T.Tuple3[A, B, C]], GIOB, func() ET.Either[L, func(C) T.Tuple3[A, B, C]], func() ET.Either[L, func(B) func(C) T.Tuple3[A, B, C]], E, L, B, func(C) T.Tuple3[A, B, C]], + Ap[GC, GTABC, func(E) func() ET.Either[L, func(C) T.Tuple3[A, B, C]], GIOC, GIOTABC, func() ET.Either[L, func(C) T.Tuple3[A, B, C]], E, L, C, T.Tuple3[A, B, C]], + + a, b, c, + ) +} + +func SequenceT4[ + GA ~func(E) GIOA, + GB ~func(E) GIOB, + GC ~func(E) GIOC, + GD ~func(E) GIOD, + GTABCD ~func(E) GIOTABCD, + GIOA ~func() ET.Either[L, A], + GIOB ~func() ET.Either[L, B], + GIOC ~func() ET.Either[L, C], + GIOD ~func() ET.Either[L, D], + GIOTABCD ~func() ET.Either[L, T.Tuple4[A, B, C, D]], + E, L, A, B, C, D any](a GA, b GB, c GC, d GD) GTABCD { + return apply.SequenceT4( + Map[GA, func(E) func() ET.Either[L, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], GIOA, func() ET.Either[L, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], E, L, A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], + Ap[GB, func(E) func() ET.Either[L, func(C) func(D) T.Tuple4[A, B, C, D]], func(E) func() ET.Either[L, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], GIOB, func() ET.Either[L, func(C) func(D) T.Tuple4[A, B, C, D]], func() ET.Either[L, func(B) func(C) func(D) T.Tuple4[A, B, C, D]], E, L, B, func(C) func(D) T.Tuple4[A, B, C, D]], + Ap[GC, func(E) func() ET.Either[L, func(D) T.Tuple4[A, B, C, D]], func(E) func() ET.Either[L, func(C) func(D) T.Tuple4[A, B, C, D]], GIOC, func() ET.Either[L, func(D) T.Tuple4[A, B, C, D]], func() ET.Either[L, func(C) func(D) T.Tuple4[A, B, C, D]], E, L, C, func(D) T.Tuple4[A, B, C, D]], + Ap[GD, GTABCD, func(E) func() ET.Either[L, func(D) T.Tuple4[A, B, C, D]], GIOD, GIOTABCD, func() ET.Either[L, func(D) T.Tuple4[A, B, C, D]], E, L, D, T.Tuple4[A, B, C, D]], + + a, b, c, d, + ) +} diff --git a/v2/readerioeither/generic/sync.go b/v2/readerioeither/generic/sync.go new file mode 100644 index 0000000..fa89a35 --- /dev/null +++ b/v2/readerioeither/generic/sync.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "context" + + ET "github.com/IBM/fp-go/v2/either" + G "github.com/IBM/fp-go/v2/readerio/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[GEA ~func(R) GIOA, GIOA ~func() ET.Either[E, A], R, E, A any](lock func() context.CancelFunc) func(fa GEA) GEA { + return G.WithLock[GEA](lock) +} diff --git a/v2/readerioeither/generic/traverse.go b/v2/readerioeither/generic/traverse.go new file mode 100644 index 0000000..ab940da --- /dev/null +++ b/v2/readerioeither/generic/traverse.go @@ -0,0 +1,100 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + RA "github.com/IBM/fp-go/v2/internal/array" + RR "github.com/IBM/fp-go/v2/internal/record" +) + +// MonadTraverseArray transforms an array +func MonadTraverseArray[GB ~func(E) GIOB, GBS ~func(E) GIOBS, GIOB ~func() ET.Either[L, B], GIOBS ~func() ET.Either[L, BBS], AAS ~[]A, BBS ~[]B, E, L, A, B any](ma AAS, f func(A) GB) GBS { + return RA.MonadTraverse[AAS]( + Of[GBS, GIOBS, E, L, BBS], + Map[GBS, func(E) func() ET.Either[L, func(B) BBS], GIOBS, func() ET.Either[L, func(B) BBS], E, L, BBS, func(B) BBS], + Ap[GB, GBS, func(E) func() ET.Either[L, func(B) BBS], GIOB, GIOBS, func() ET.Either[L, func(B) BBS], E, L, B, BBS], + + ma, f, + ) +} + +// TraverseArray transforms an array +func TraverseArray[GB ~func(E) GIOB, GBS ~func(E) GIOBS, GIOB ~func() ET.Either[L, B], GIOBS ~func() ET.Either[L, BBS], AAS ~[]A, BBS ~[]B, E, L, A, B any](f func(A) GB) func(AAS) GBS { + return RA.Traverse[AAS]( + Of[GBS, GIOBS, E, L, BBS], + Map[GBS, func(E) func() ET.Either[L, func(B) BBS], GIOBS, func() ET.Either[L, func(B) BBS], E, L, BBS, func(B) BBS], + Ap[GB, GBS, func(E) func() ET.Either[L, func(B) BBS], GIOB, GIOBS, func() ET.Either[L, func(B) BBS], E, L, B, BBS], + + f, + ) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[GB ~func(E) GIOB, GBS ~func(E) GIOBS, GIOB ~func() ET.Either[L, B], GIOBS ~func() ET.Either[L, BBS], AAS ~[]A, BBS ~[]B, E, L, A, B any](f func(int, A) GB) func(AAS) GBS { + return RA.TraverseWithIndex[AAS]( + Of[GBS, GIOBS, E, L, BBS], + Map[GBS, func(E) func() ET.Either[L, func(B) BBS], GIOBS, func() ET.Either[L, func(B) BBS], E, L, BBS, func(B) BBS], + Ap[GB, GBS, func(E) func() ET.Either[L, func(B) BBS], GIOB, GIOBS, func() ET.Either[L, func(B) BBS], E, L, B, BBS], + + f, + ) +} + +// SequenceArray converts a homogeneous sequence of either into an either of sequence +func SequenceArray[GA ~func(E) GIOA, GAS ~func(E) GIOAS, GIOA ~func() ET.Either[L, A], GIOAS ~func() ET.Either[L, AAS], AAS ~[]A, GAAS ~[]GA, E, L, A any](ma GAAS) GAS { + return MonadTraverseArray[GA, GAS](ma, F.Identity[GA]) +} + +// MonadTraverseRecord transforms an array +func MonadTraverseRecord[GB ~func(C) GIOB, GBS ~func(C) GIOBS, GIOB ~func() ET.Either[E, B], GIOBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, C, E, A, B any](tas AAS, f func(A) GB) GBS { + return RR.MonadTraverse[AAS]( + Of[GBS, GIOBS, C, E, BBS], + Map[GBS, func(C) func() ET.Either[E, func(B) BBS], GIOBS, func() ET.Either[E, func(B) BBS], C, E, BBS, func(B) BBS], + Ap[GB, GBS, func(C) func() ET.Either[E, func(B) BBS], GIOB, GIOBS, func() ET.Either[E, func(B) BBS], C, E, B, BBS], + + tas, + f, + ) +} + +// TraverseRecord transforms a record +func TraverseRecord[GB ~func(C) GIOB, GBS ~func(C) GIOBS, GIOB ~func() ET.Either[E, B], GIOBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, C, E, A, B any](f func(A) GB) func(AAS) GBS { + return RR.Traverse[AAS]( + Of[GBS, GIOBS, C, E, BBS], + Map[GBS, func(C) func() ET.Either[E, func(B) BBS], GIOBS, func() ET.Either[E, func(B) BBS], C, E, BBS, func(B) BBS], + Ap[GB, GBS, func(C) func() ET.Either[E, func(B) BBS], GIOB, GIOBS, func() ET.Either[E, func(B) BBS], C, E, B, BBS], + + f, + ) +} + +// TraverseRecordWithIndex transforms a record +func TraverseRecordWithIndex[GB ~func(C) GIOB, GBS ~func(C) GIOBS, GIOB ~func() ET.Either[E, B], GIOBS ~func() ET.Either[E, BBS], AAS ~map[K]A, BBS ~map[K]B, K comparable, C, E, A, B any](f func(K, A) GB) func(AAS) GBS { + return RR.TraverseWithIndex[AAS]( + Of[GBS, GIOBS, C, E, BBS], + Map[GBS, func(C) func() ET.Either[E, func(B) BBS], GIOBS, func() ET.Either[E, func(B) BBS], C, E, BBS, func(B) BBS], + Ap[GB, GBS, func(C) func() ET.Either[E, func(B) BBS], GIOB, GIOBS, func() ET.Either[E, func(B) BBS], C, E, B, BBS], + + f, + ) +} + +// SequenceRecord converts a homogeneous sequence of either into an either of sequence +func SequenceRecord[GA ~func(C) GIOA, GAS ~func(C) GIOAS, GIOA ~func() ET.Either[E, A], GIOAS ~func() ET.Either[E, AAS], AAS ~map[K]A, GAAS ~map[K]GA, K comparable, C, E, A any](tas GAAS) GAS { + return MonadTraverseRecord[GA, GAS](tas, F.Identity[GA]) +} diff --git a/v2/readerioeither/monad.go b/v2/readerioeither/monad.go new file mode 100644 index 0000000..7b6b9e8 --- /dev/null +++ b/v2/readerioeither/monad.go @@ -0,0 +1,38 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// Pointed returns the pointed operations for [ReaderIOEither] +func Pointed[R, E, A any]() pointed.Pointed[A, ReaderIOEither[R, E, A]] { + return G.Pointed[R, E, A, ReaderIOEither[R, E, A]]() +} + +// Functor returns the functor operations for [ReaderIOEither] +func Functor[R, E, A, B any]() functor.Functor[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]] { + return G.Functor[R, E, A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]]() +} + +// Monad returns the monadic operations for [ReaderIOEither] +func Monad[R, E, A, B any]() monad.Monad[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]] { + return G.Monad[R, E, A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]]() +} diff --git a/v2/readerioeither/reader.go b/v2/readerioeither/reader.go new file mode 100644 index 0000000..a63981f --- /dev/null +++ b/v2/readerioeither/reader.go @@ -0,0 +1,297 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/io" + IOE "github.com/IBM/fp-go/v2/ioeither" + L "github.com/IBM/fp-go/v2/lazy" + O "github.com/IBM/fp-go/v2/option" + RD "github.com/IBM/fp-go/v2/reader" + RE "github.com/IBM/fp-go/v2/readereither" + RIO "github.com/IBM/fp-go/v2/readerio" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +type ReaderIOEither[R, E, A any] RD.Reader[R, IOE.IOEither[E, A]] + +// MakeReader constructs an instance of a reader +func MakeReader[R, E, A any](f func(R) IOE.IOEither[E, A]) ReaderIOEither[R, E, A] { + return G.MakeReader[ReaderIOEither[R, E, A]](f) +} + +func MonadFromReaderIO[R, E, A any](a A, f func(A) RIO.ReaderIO[R, A]) ReaderIOEither[R, E, A] { + return G.MonadFromReaderIO[ReaderIOEither[R, E, A]](a, f) +} + +func FromReaderIO[R, E, A any](f func(A) RIO.ReaderIO[R, A]) func(A) ReaderIOEither[R, E, A] { + return G.FromReaderIO[ReaderIOEither[R, E, A]](f) +} + +func RightReaderIO[R, E, A any](ma RIO.ReaderIO[R, A]) ReaderIOEither[R, E, A] { + return G.RightReaderIO[ReaderIOEither[R, E, A]](ma) +} + +func LeftReaderIO[A, R, E any](me RIO.ReaderIO[R, E]) ReaderIOEither[R, E, A] { + return G.LeftReaderIO[ReaderIOEither[R, E, A]](me) +} + +func MonadMap[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) B) ReaderIOEither[R, E, B] { + return G.MonadMap[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](fa, f) +} + +func Map[R, E, A, B any](f func(A) B) func(fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.Map[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](f) +} + +func MonadMapTo[R, E, A, B any](fa ReaderIOEither[R, E, A], b B) ReaderIOEither[R, E, B] { + return G.MonadMapTo[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](fa, b) +} + +func MapTo[R, E, A, B any](b B) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.MapTo[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](b) +} + +func MonadChain[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEither[R, E, B]) ReaderIOEither[R, E, B] { + return G.MonadChain(fa, f) +} + +func MonadChainFirst[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEither[R, E, B]) ReaderIOEither[R, E, A] { + return G.MonadChainFirst(fa, f) +} + +func MonadChainEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) ET.Either[E, B]) ReaderIOEither[R, E, B] { + return G.MonadChainEitherK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](ma, f) +} + +func ChainEitherK[R, E, A, B any](f func(A) ET.Either[E, B]) func(ma ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.ChainEitherK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](f) +} + +func MonadChainFirstEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) ET.Either[E, B]) ReaderIOEither[R, E, A] { + return G.MonadChainFirstEitherK[ReaderIOEither[R, E, A]](ma, f) +} + +func ChainFirstEitherK[R, E, A, B any](f func(A) ET.Either[E, B]) func(ma ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.ChainFirstEitherK[ReaderIOEither[R, E, A]](f) +} + +func MonadChainReaderK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) RD.Reader[R, B]) ReaderIOEither[R, E, B] { + return G.MonadChainReaderK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](ma, f) +} + +func ChainReaderK[E, R, A, B any](f func(A) RD.Reader[R, B]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.ChainReaderK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](f) +} + +func MonadChainIOEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) IOE.IOEither[E, B]) ReaderIOEither[R, E, B] { + return G.MonadChainIOEitherK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](ma, f) +} + +func ChainIOEitherK[R, E, A, B any](f func(A) IOE.IOEither[E, B]) func(ma ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.ChainIOEitherK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](f) +} + +func MonadChainIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io.IO[B]) ReaderIOEither[R, E, B] { + return G.MonadChainIOK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](ma, f) +} + +func ChainIOK[R, E, A, B any](f func(A) io.IO[B]) func(ma ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.ChainIOK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](f) +} + +func MonadChainFirstIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io.IO[B]) ReaderIOEither[R, E, A] { + return G.MonadChainFirstIOK[ReaderIOEither[R, E, A]](ma, f) +} + +func ChainFirstIOK[R, E, A, B any](f func(A) io.IO[B]) func(ma ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.ChainFirstIOK[ReaderIOEither[R, E, A]](f) +} + +func ChainOptionK[R, A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.ChainOptionK[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](onNone) +} + +func MonadAp[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.MonadAp[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]](fab, fa) +} + +func Ap[B, R, E, A any](fa ReaderIOEither[R, E, A]) func(fab ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B] { + return G.Ap[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]](fa) +} + +func Chain[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] { + return G.Chain[ReaderIOEither[R, E, A]](f) +} + +func ChainFirst[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.ChainFirst[ReaderIOEither[R, E, A]](f) +} + +func Right[R, E, A any](a A) ReaderIOEither[R, E, A] { + return G.Right[ReaderIOEither[R, E, A]](a) +} + +func Left[R, A, E any](e E) ReaderIOEither[R, E, A] { + return G.Left[ReaderIOEither[R, E, A]](e) +} + +func ThrowError[R, A, E any](e E) ReaderIOEither[R, E, A] { + return G.ThrowError[ReaderIOEither[R, E, A]](e) +} + +// Of returns a Reader with a fixed value +func Of[R, E, A any](a A) ReaderIOEither[R, E, A] { + return G.Of[ReaderIOEither[R, E, A]](a) +} + +func Flatten[R, E, A any](mma ReaderIOEither[R, E, ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A] { + return G.Flatten(mma) +} + +func FromEither[R, E, A any](t ET.Either[E, A]) ReaderIOEither[R, E, A] { + return G.FromEither[ReaderIOEither[R, E, A]](t) +} + +func RightReader[E, R, A any](ma RD.Reader[R, A]) ReaderIOEither[R, E, A] { + return G.RightReader[RD.Reader[R, A], ReaderIOEither[R, E, A]](ma) +} + +func LeftReader[A, R, E any](ma RD.Reader[R, E]) ReaderIOEither[R, E, A] { + return G.LeftReader[RD.Reader[R, E], ReaderIOEither[R, E, A]](ma) +} + +func FromReader[E, R, A any](ma RD.Reader[R, A]) ReaderIOEither[R, E, A] { + return G.FromReader[RD.Reader[R, A], ReaderIOEither[R, E, A]](ma) +} + +func RightIO[R, E, A any](ma io.IO[A]) ReaderIOEither[R, E, A] { + return G.RightIO[ReaderIOEither[R, E, A]](ma) +} + +func LeftIO[R, A, E any](ma io.IO[E]) ReaderIOEither[R, E, A] { + return G.LeftIO[ReaderIOEither[R, E, A]](ma) +} + +func FromIO[R, E, A any](ma io.IO[A]) ReaderIOEither[R, E, A] { + return G.FromIO[ReaderIOEither[R, E, A]](ma) +} + +func FromIOEither[R, E, A any](ma IOE.IOEither[E, A]) ReaderIOEither[R, E, A] { + return G.FromIOEither[ReaderIOEither[R, E, A]](ma) +} + +func FromReaderEither[R, E, A any](ma RE.ReaderEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.FromReaderEither[RE.ReaderEither[R, E, A], ReaderIOEither[R, E, A]](ma) +} + +func Ask[R, E any]() ReaderIOEither[R, E, R] { + return G.Ask[ReaderIOEither[R, E, R]]() +} + +func Asks[E, R, A any](r RD.Reader[R, A]) ReaderIOEither[R, E, A] { + return G.Asks[RD.Reader[R, A], ReaderIOEither[R, E, A]](r) +} + +func FromOption[R, A, E any](onNone func() E) func(O.Option[A]) ReaderIOEither[R, E, A] { + return G.FromOption[ReaderIOEither[R, E, A]](onNone) +} + +func FromPredicate[R, E, A any](pred func(A) bool, onFalse func(A) E) func(A) ReaderIOEither[R, E, A] { + return G.FromPredicate[ReaderIOEither[R, E, A]](pred, onFalse) +} + +func Fold[R, E, A, B any](onLeft func(E) RIO.ReaderIO[R, B], onRight func(A) RIO.ReaderIO[R, B]) func(ReaderIOEither[R, E, A]) RIO.ReaderIO[R, B] { + return G.Fold[RIO.ReaderIO[R, B], ReaderIOEither[R, E, A]](onLeft, onRight) +} + +func GetOrElse[R, E, A any](onLeft func(E) RIO.ReaderIO[R, A]) func(ReaderIOEither[R, E, A]) RIO.ReaderIO[R, A] { + return G.GetOrElse[RIO.ReaderIO[R, A], ReaderIOEither[R, E, A]](onLeft) +} + +func OrElse[R, E1, A, E2 any](onLeft func(E1) ReaderIOEither[R, E2, A]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] { + return G.OrElse[ReaderIOEither[R, E1, A]](onLeft) +} + +func OrLeft[A, E1, R, E2 any](onLeft func(E1) RIO.ReaderIO[R, E2]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] { + return G.OrLeft[ReaderIOEither[R, E1, A], RIO.ReaderIO[R, E2], ReaderIOEither[R, E2, A]](onLeft) +} + +func MonadBiMap[R, E1, E2, A, B any](fa ReaderIOEither[R, E1, A], f func(E1) E2, g func(A) B) ReaderIOEither[R, E2, B] { + return G.MonadBiMap[ReaderIOEither[R, E1, A], ReaderIOEither[R, E2, B]](fa, f, g) +} + +// BiMap maps a pair of functions over the two type arguments of the bifunctor. +func BiMap[R, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, B] { + return G.BiMap[ReaderIOEither[R, E1, A], ReaderIOEither[R, E2, B]](f, g) +} + +// Swap changes the order of type parameters +func Swap[R, E, A any](val ReaderIOEither[R, E, A]) ReaderIOEither[R, A, E] { + return G.Swap[ReaderIOEither[R, E, A], ReaderIOEither[R, A, E]](val) +} + +// Defer creates an IO by creating a brand new IO via a generator function, each time +func Defer[R, E, A any](gen L.Lazy[ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A] { + return G.Defer[ReaderIOEither[R, E, A]](gen) +} + +// TryCatch wraps a reader returning a tuple as an error into ReaderIOEither +func TryCatch[R, E, A any](f func(R) func() (A, error), onThrow func(error) E) ReaderIOEither[R, E, A] { + return G.TryCatch[ReaderIOEither[R, E, A]](f, onThrow) +} + +// MonadAlt identifies an associative operation on a type constructor. +func MonadAlt[R, E, A any](first ReaderIOEither[R, E, A], second L.Lazy[ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A] { + return G.MonadAlt(first, second) +} + +// Alt identifies an associative operation on a type constructor. +func Alt[R, E, A any](second L.Lazy[ReaderIOEither[R, E, A]]) func(ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.Alt(second) +} + +// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once +// The context used to compute the value is the context of the first call, so do not use this +// method if the value has a functional dependency on the content of the context +func Memoize[ + R, E, A any](rdr ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.Memoize[ReaderIOEither[R, E, A]](rdr) +} + +func MonadFlap[R, E, B, A any](fab ReaderIOEither[R, E, func(A) B], a A) ReaderIOEither[R, E, B] { + return G.MonadFlap[ReaderIOEither[R, E, func(A) B], ReaderIOEither[R, E, B]](fab, a) +} + +func Flap[R, E, B, A any](a A) func(ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B] { + return G.Flap[ReaderIOEither[R, E, func(A) B], ReaderIOEither[R, E, B]](a) +} + +func MonadMapLeft[R, E1, E2, A any](fa ReaderIOEither[R, E1, A], f func(E1) E2) ReaderIOEither[R, E2, A] { + return G.MonadMapLeft[ReaderIOEither[R, E1, A], ReaderIOEither[R, E2, A]](fa, f) +} + +// MapLeft applies a mapping function to the error channel +func MapLeft[R, A, E1, E2 any](f func(E1) E2) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] { + return G.MapLeft[ReaderIOEither[R, E1, A], ReaderIOEither[R, E2, A]](f) +} + +// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s +// `contramap`). +func Local[R1, R2, E, A any](f func(R2) R1) func(ReaderIOEither[R1, E, A]) ReaderIOEither[R2, E, A] { + return G.Local[ReaderIOEither[R1, E, A], ReaderIOEither[R2, E, A]](f) +} diff --git a/v2/readerioeither/reader_test.go b/v2/readerioeither/reader_test.go new file mode 100644 index 0000000..87a8106 --- /dev/null +++ b/v2/readerioeither/reader_test.go @@ -0,0 +1,79 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + "fmt" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/utils" + R "github.com/IBM/fp-go/v2/reader" + RIO "github.com/IBM/fp-go/v2/readerio" + "github.com/stretchr/testify/assert" +) + +func TestMap(t *testing.T) { + + g := F.Pipe1( + Of[context.Context, error](1), + Map[context.Context, error](utils.Double), + ) + + assert.Equal(t, E.Of[error](2), g(context.Background())()) +} + +func TestOrLeft(t *testing.T) { + f := OrLeft[int](func(s string) RIO.ReaderIO[context.Context, string] { + return RIO.Of[context.Context](s + "!") + }) + + g1 := F.Pipe1( + Right[context.Context, string](1), + f, + ) + + g2 := F.Pipe1( + Left[context.Context, int]("a"), + f, + ) + + assert.Equal(t, E.Of[string](1), g1(context.Background())()) + assert.Equal(t, E.Left[int]("a!"), g2(context.Background())()) +} + +func TestAp(t *testing.T) { + g := F.Pipe1( + Right[context.Context, error](utils.Double), + Ap[int](Right[context.Context, error](1)), + ) + + assert.Equal(t, E.Right[error](2), g(context.Background())()) +} + +func TestChainReaderK(t *testing.T) { + + g := F.Pipe1( + Of[context.Context, error](1), + ChainReaderK[error](func(v int) R.Reader[context.Context, string] { + return R.Of[context.Context](fmt.Sprintf("%d", v)) + }), + ) + + assert.Equal(t, E.Right[error]("1"), g(context.Background())()) +} diff --git a/v2/readerioeither/resource.go b/v2/readerioeither/resource.go new file mode 100644 index 0000000..6006e96 --- /dev/null +++ b/v2/readerioeither/resource.go @@ -0,0 +1,25 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// WithResource constructs a function that creates a resource, then operates on it and then releases the resource +func WithResource[A, L, E, R any](onCreate ReaderIOEither[L, E, R], onRelease func(R) ReaderIOEither[L, E, any]) func(func(R) ReaderIOEither[L, E, A]) ReaderIOEither[L, E, A] { + return G.WithResource[ReaderIOEither[L, E, A]](onCreate, onRelease) +} diff --git a/v2/readerioeither/sequence.go b/v2/readerioeither/sequence.go new file mode 100644 index 0000000..80be92c --- /dev/null +++ b/v2/readerioeither/sequence.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + G "github.com/IBM/fp-go/v2/readerioeither/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple + +func SequenceT1[R, E, A any](a ReaderIOEither[R, E, A]) ReaderIOEither[R, E, T.Tuple1[A]] { + return G.SequenceT1[ + ReaderIOEither[R, E, A], + ReaderIOEither[R, E, T.Tuple1[A]], + ](a) +} + +func SequenceT2[R, E, A, B any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B]) ReaderIOEither[R, E, T.Tuple2[A, B]] { + return G.SequenceT2[ + ReaderIOEither[R, E, A], + ReaderIOEither[R, E, B], + ReaderIOEither[R, E, T.Tuple2[A, B]], + ](a, b) +} + +func SequenceT3[R, E, A, B, C any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B], c ReaderIOEither[R, E, C]) ReaderIOEither[R, E, T.Tuple3[A, B, C]] { + return G.SequenceT3[ + ReaderIOEither[R, E, A], + ReaderIOEither[R, E, B], + ReaderIOEither[R, E, C], + ReaderIOEither[R, E, T.Tuple3[A, B, C]], + ](a, b, c) +} + +func SequenceT4[R, E, A, B, C, D any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B], c ReaderIOEither[R, E, C], d ReaderIOEither[R, E, D]) ReaderIOEither[R, E, T.Tuple4[A, B, C, D]] { + return G.SequenceT4[ + ReaderIOEither[R, E, A], + ReaderIOEither[R, E, B], + ReaderIOEither[R, E, C], + ReaderIOEither[R, E, D], + ReaderIOEither[R, E, T.Tuple4[A, B, C, D]], + ](a, b, c, d) +} diff --git a/v2/readerioeither/sequence_test.go b/v2/readerioeither/sequence_test.go new file mode 100644 index 0000000..925540c --- /dev/null +++ b/v2/readerioeither/sequence_test.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + "testing" + + ET "github.com/IBM/fp-go/v2/either" + T "github.com/IBM/fp-go/v2/tuple" + "github.com/stretchr/testify/assert" +) + +func TestSequence2(t *testing.T) { + // two readers of heterogeneous types + first := Of[context.Context, error]("a") + second := Of[context.Context, error](1) + + // compose + s2 := SequenceT2[context.Context, error, string, int] + res := s2(first, second) + + ctx := context.Background() + assert.Equal(t, ET.Right[error](T.MakeTuple2("a", 1)), res(ctx)()) +} diff --git a/v2/readerioeither/sync.go b/v2/readerioeither/sync.go new file mode 100644 index 0000000..e3cba82 --- /dev/null +++ b/v2/readerioeither/sync.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + "context" + + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// WithLock executes the provided IO operation in the scope of a lock +func WithLock[R, E, A any](lock func() context.CancelFunc) func(fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] { + return G.WithLock[ReaderIOEither[R, E, A]](lock) +} diff --git a/v2/readerioeither/traverse.go b/v2/readerioeither/traverse.go new file mode 100644 index 0000000..83561c8 --- /dev/null +++ b/v2/readerioeither/traverse.go @@ -0,0 +1,51 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readerioeither + +import ( + IOE "github.com/IBM/fp-go/v2/ioeither" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// TraverseArray transforms an array +func TraverseArray[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func([]A) ReaderIOEither[R, E, []B] { + return G.TraverseArray[ReaderIOEither[R, E, B], ReaderIOEither[R, E, []B], IOE.IOEither[E, B], IOE.IOEither[E, []B], []A](f) +} + +// TraverseArrayWithIndex transforms an array +func TraverseArrayWithIndex[R, E, A, B any](f func(int, A) ReaderIOEither[R, E, B]) func([]A) ReaderIOEither[R, E, []B] { + return G.TraverseArrayWithIndex[ReaderIOEither[R, E, B], ReaderIOEither[R, E, []B], IOE.IOEither[E, B], IOE.IOEither[E, []B], []A](f) +} + +// SequenceArray converts a homogeneous sequence of Readers into a Reader of a sequence +func SequenceArray[R, E, A any](ma []ReaderIOEither[R, E, A]) ReaderIOEither[R, E, []A] { + return G.SequenceArray[ReaderIOEither[R, E, A], ReaderIOEither[R, E, []A]](ma) +} + +// TraverseRecord transforms an array +func TraverseRecord[R any, K comparable, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] { + return G.TraverseRecord[ReaderIOEither[R, E, B], ReaderIOEither[R, E, map[K]B], IOE.IOEither[E, B], IOE.IOEither[E, map[K]B], map[K]A](f) +} + +// TraverseRecordWithIndex transforms an array +func TraverseRecordWithIndex[R any, K comparable, E, A, B any](f func(K, A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] { + return G.TraverseRecordWithIndex[ReaderIOEither[R, E, B], ReaderIOEither[R, E, map[K]B], IOE.IOEither[E, B], IOE.IOEither[E, map[K]B], map[K]A](f) +} + +// SequenceRecord converts a homogeneous sequence of Readers into a Reader of a sequence +func SequenceRecord[R any, K comparable, E, A any](ma map[K]ReaderIOEither[R, E, A]) ReaderIOEither[R, E, map[K]A] { + return G.SequenceRecord[ReaderIOEither[R, E, A], ReaderIOEither[R, E, map[K]A]](ma) +} diff --git a/v2/record/bind.go b/v2/record/bind.go new file mode 100644 index 0000000..8296803 --- /dev/null +++ b/v2/record/bind.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + Mo "github.com/IBM/fp-go/v2/monoid" + G "github.com/IBM/fp-go/v2/record/generic" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[K comparable, S any]() map[K]S { + return G.Do[map[K]S, K, S]() +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[S1, T any, K comparable, S2 any](m Mo.Monoid[map[K]S2]) func(setter func(T) func(S1) S2, f func(S1) map[K]T) func(map[K]S1) map[K]S2 { + return G.Bind[map[K]S1, map[K]S2, map[K]T, K, S1, S2, T](m) +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[S1, T any, K comparable, S2 any]( + setter func(T) func(S1) S2, + f func(S1) T, +) func(map[K]S1) map[K]S2 { + return G.Let[map[K]S1, map[K]S2, K, S1, S2, T](setter, f) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[S1, T any, K comparable, S2 any]( + setter func(T) func(S1) S2, + b T, +) func(map[K]S1) map[K]S2 { + return G.LetTo[map[K]S1, map[K]S2, K, S1, S2, T](setter, b) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[S1, T any, K comparable](setter func(T) S1) func(map[K]T) map[K]S1 { + return G.BindTo[map[K]S1, map[K]T, K, S1, T](setter) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[S1, T any, K comparable, S2 any](m Mo.Monoid[map[K]S2]) func(setter func(T) func(S1) S2, fa map[K]T) func(map[K]S1) map[K]S2 { + return G.ApS[map[K]S1, map[K]S2, map[K]T, K, S1, S2, T](m) +} diff --git a/v2/record/doc.go b/v2/record/doc.go new file mode 100644 index 0000000..76d6372 --- /dev/null +++ b/v2/record/doc.go @@ -0,0 +1,17 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package record contains monadic operations for maps as well as a rich set of utility functions +package record diff --git a/v2/record/eq.go b/v2/record/eq.go new file mode 100644 index 0000000..d7d9c40 --- /dev/null +++ b/v2/record/eq.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + E "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/record/generic" +) + +func Eq[K comparable, V any](e E.Eq[V]) E.Eq[map[K]V] { + return G.Eq[map[K]V, K, V](e) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[K, V comparable]() E.Eq[map[K]V] { + return G.FromStrictEquals[map[K]V]() +} diff --git a/v2/record/eq_test.go b/v2/record/eq_test.go new file mode 100644 index 0000000..3619c9c --- /dev/null +++ b/v2/record/eq_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFromStrictEquals(t *testing.T) { + m1 := map[string]string{ + "a": "A", + "b": "B", + } + m2 := map[string]string{ + "a": "A", + "b": "C", + } + m3 := map[string]string{ + "a": "A", + "b": "B", + } + m4 := map[string]string{ + "a": "A", + "b": "B", + "c": "C", + } + + e := FromStrictEquals[string, string]() + assert.True(t, e.Equals(m1, m1)) + assert.True(t, e.Equals(m1, m3)) + assert.False(t, e.Equals(m1, m2)) + assert.False(t, e.Equals(m1, m4)) +} diff --git a/v2/record/generic/bind.go b/v2/record/generic/bind.go new file mode 100644 index 0000000..f888247 --- /dev/null +++ b/v2/record/generic/bind.go @@ -0,0 +1,86 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + A "github.com/IBM/fp-go/v2/internal/apply" + C "github.com/IBM/fp-go/v2/internal/chain" + F "github.com/IBM/fp-go/v2/internal/functor" + Mo "github.com/IBM/fp-go/v2/monoid" +) + +// Bind creates an empty context of type [S] to be used with the [Bind] operation +func Do[GS ~map[K]S, K comparable, S any]() GS { + return Empty[GS, K, S]() +} + +// Bind attaches the result of a computation to a context [S1] to produce a context [S2] +func Bind[GS1 ~map[K]S1, GS2 ~map[K]S2, GT ~map[K]T, K comparable, S1, S2, T any](m Mo.Monoid[GS2]) func(setter func(T) func(S1) S2, f func(S1) GT) func(GS1) GS2 { + c := Chain[GS1, GS2, K, S1, S2](m) + return func(setter func(T) func(S1) S2, f func(S1) GT) func(GS1) GS2 { + return C.Bind( + c, + Map[GT, GS2, K, T, S2], + setter, + f, + ) + } +} + +// Let attaches the result of a computation to a context [S1] to produce a context [S2] +func Let[GS1 ~map[K]S1, GS2 ~map[K]S2, K comparable, S1, S2, T any]( + key func(T) func(S1) S2, + f func(S1) T, +) func(GS1) GS2 { + return F.Let( + Map[GS1, GS2, K, S1, S2], + key, + f, + ) +} + +// LetTo attaches the a value to a context [S1] to produce a context [S2] +func LetTo[GS1 ~map[K]S1, GS2 ~map[K]S2, K comparable, S1, S2, B any]( + key func(B) func(S1) S2, + b B, +) func(GS1) GS2 { + return F.LetTo( + Map[GS1, GS2, K, S1, S2], + key, + b, + ) +} + +// BindTo initializes a new state [S1] from a value [T] +func BindTo[GS1 ~map[K]S1, GT ~map[K]T, K comparable, S1, T any](setter func(T) S1) func(GT) GS1 { + return C.BindTo( + Map[GT, GS1, K, T, S1], + setter, + ) +} + +// ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently +func ApS[GS1 ~map[K]S1, GS2 ~map[K]S2, GT ~map[K]T, K comparable, S1, S2, T any](m Mo.Monoid[GS2]) func(setter func(T) func(S1) S2, fa GT) func(GS1) GS2 { + a := Ap[GS2, map[K]func(T) S2, GT, K, S2, T](m) + return func(setter func(T) func(S1) S2, fa GT) func(GS1) GS2 { + return A.ApS( + a, + Map[GS1, map[K]func(T) S2, K, S1, func(T) S2], + setter, + fa, + ) + } +} diff --git a/v2/record/generic/eq.go b/v2/record/generic/eq.go new file mode 100644 index 0000000..3be4baa --- /dev/null +++ b/v2/record/generic/eq.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + E "github.com/IBM/fp-go/v2/eq" +) + +func equals[M ~map[K]V, K comparable, V any](left, right M, eq func(V, V) bool) bool { + if len(left) != len(right) { + return false + } + for k, v1 := range left { + if v2, ok := right[k]; !ok || !eq(v1, v2) { + return false + } + } + return true +} + +func Eq[M ~map[K]V, K comparable, V any](e E.Eq[V]) E.Eq[M] { + eq := e.Equals + return E.FromEquals(func(left, right M) bool { + return equals(left, right, eq) + }) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[M ~map[K]V, K, V comparable]() E.Eq[M] { + return Eq[M](E.FromStrictEquals[V]()) +} diff --git a/v2/record/generic/monoid.go b/v2/record/generic/monoid.go new file mode 100644 index 0000000..e9bbb19 --- /dev/null +++ b/v2/record/generic/monoid.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func UnionMonoid[N ~map[K]V, K comparable, V any](s S.Semigroup[V]) M.Monoid[N] { + return M.MakeMonoid( + UnionSemigroup[N](s).Concat, + Empty[N](), + ) +} + +func UnionLastMonoid[N ~map[K]V, K comparable, V any]() M.Monoid[N] { + return M.MakeMonoid( + unionLast[N], + Empty[N](), + ) +} + +func UnionFirstMonoid[N ~map[K]V, K comparable, V any]() M.Monoid[N] { + return M.MakeMonoid( + F.Swap(unionLast[N]), + Empty[N](), + ) +} diff --git a/v2/record/generic/record.go b/v2/record/generic/record.go new file mode 100644 index 0000000..fd88537 --- /dev/null +++ b/v2/record/generic/record.go @@ -0,0 +1,611 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "sort" + + F "github.com/IBM/fp-go/v2/function" + RAG "github.com/IBM/fp-go/v2/internal/array" + FC "github.com/IBM/fp-go/v2/internal/functor" + G "github.com/IBM/fp-go/v2/internal/record" + Mg "github.com/IBM/fp-go/v2/magma" + Mo "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/ord" + T "github.com/IBM/fp-go/v2/tuple" +) + +func IsEmpty[M ~map[K]V, K comparable, V any](r M) bool { + return len(r) == 0 +} + +func IsNonEmpty[M ~map[K]V, K comparable, V any](r M) bool { + return len(r) > 0 +} + +func Keys[M ~map[K]V, GK ~[]K, K comparable, V any](r M) GK { + return collect[M, GK](r, F.First[K, V]) +} + +func Values[M ~map[K]V, GV ~[]V, K comparable, V any](r M) GV { + return collect[M, GV](r, F.Second[K, V]) +} + +func KeysOrd[M ~map[K]V, GK ~[]K, K comparable, V any](o ord.Ord[K]) func(r M) GK { + return func(r M) GK { + return collectOrd[M, GK](o, r, F.First[K, V]) + } +} + +func ValuesOrd[M ~map[K]V, GV ~[]V, K comparable, V any](o ord.Ord[K]) func(r M) GV { + return func(r M) GV { + return collectOrd[M, GV](o, r, F.Second[K, V]) + } +} + +func collectOrd[M ~map[K]V, GR ~[]R, K comparable, V, R any](o ord.Ord[K], r M, f func(K, V) R) GR { + // create the entries + entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r) + // collect this array + ft := T.Tupled2(f) + count := len(entries) + result := make(GR, count) + for i := count - 1; i >= 0; i-- { + result[i] = ft(entries[i]) + } + // done + return result +} + +func reduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K], r M, f func(K, R, V) R, initial R) R { + // create the entries + entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r) + // collect this array + current := initial + count := len(entries) + for i := 0; i < count; i++ { + t := entries[i] + current = f(T.First(t), current, T.Second(t)) + } + // done + return current +} + +func collect[M ~map[K]V, GR ~[]R, K comparable, V, R any](r M, f func(K, V) R) GR { + count := len(r) + result := make(GR, count) + idx := 0 + for k, v := range r { + result[idx] = f(k, v) + idx++ + } + return result +} + +func Collect[M ~map[K]V, GR ~[]R, K comparable, V, R any](f func(K, V) R) func(M) GR { + return F.Bind2nd(collect[M, GR, K, V, R], f) +} + +func CollectOrd[M ~map[K]V, GR ~[]R, K comparable, V, R any](o ord.Ord[K]) func(f func(K, V) R) func(M) GR { + return func(f func(K, V) R) func(M) GR { + return func(r M) GR { + return collectOrd[M, GR](o, r, f) + } + } +} + +func Reduce[M ~map[K]V, K comparable, V, R any](f func(R, V) R, initial R) func(M) R { + return func(r M) R { + return G.Reduce(r, f, initial) + } +} + +func ReduceWithIndex[M ~map[K]V, K comparable, V, R any](f func(K, R, V) R, initial R) func(M) R { + return func(r M) R { + return G.ReduceWithIndex(r, f, initial) + } +} + +func ReduceRef[M ~map[K]V, K comparable, V, R any](f func(R, *V) R, initial R) func(M) R { + return func(r M) R { + return G.ReduceRef(r, f, initial) + } +} + +func ReduceRefWithIndex[M ~map[K]V, K comparable, V, R any](f func(K, R, *V) R, initial R) func(M) R { + return func(r M) R { + return G.ReduceRefWithIndex(r, f, initial) + } +} + +func MonadAp[BS ~map[K]B, ABS ~map[K]func(A) B, AS ~map[K]A, K comparable, B, A any](m Mo.Monoid[BS], fab ABS, fa AS) BS { + return MonadChain(m, fab, F.Bind1st(MonadMap[AS, BS, K, A, B], fa)) +} + +func Ap[BS ~map[K]B, ABS ~map[K]func(A) B, AS ~map[K]A, K comparable, B, A any](m Mo.Monoid[BS]) func(fa AS) func(ABS) BS { + return func(ma AS) func(ABS) BS { + return func(abs ABS) BS { + return MonadAp(m, abs, ma) + } + } +} + +func MonadMap[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(V) R) N { + return MonadMapWithIndex[M, N](r, F.Ignore1of2[K](f)) +} + +func MonadChainWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N], r M, f func(K, V1) N) N { + return G.ReduceWithIndex(r, func(k K, dst N, b V1) N { + return m.Concat(dst, f(k, b)) + }, m.Empty()) +} + +func MonadChain[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N], r M, f func(V1) N) N { + return G.Reduce(r, func(dst N, b V1) N { + return m.Concat(dst, f(b)) + }, m.Empty()) +} + +func ChainWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N]) func(func(K, V1) N) func(M) N { + return func(f func(K, V1) N) func(M) N { + return func(ma M) N { + return MonadChainWithIndex(m, ma, f) + } + } +} + +func Chain[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N]) func(func(V1) N) func(M) N { + return func(f func(V1) N) func(M) N { + return func(ma M) N { + return MonadChain(m, ma, f) + } + } +} + +func MonadMapWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(K, V) R) N { + return G.ReduceWithIndex(r, func(k K, dst N, v V) N { + return upsertAtReadWrite(dst, k, f(k, v)) + }, make(N, len(r))) +} + +func MonadMapRefWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(K, *V) R) N { + return G.ReduceRefWithIndex(r, func(k K, dst N, v *V) N { + return upsertAtReadWrite(dst, k, f(k, v)) + }, make(N, len(r))) +} + +func MonadMapRef[M ~map[K]V, N ~map[K]R, K comparable, V, R any](r M, f func(*V) R) N { + return MonadMapRefWithIndex[M, N](r, F.Ignore1of2[K](f)) +} + +func Map[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(V) R) func(M) N { + return F.Bind2nd(MonadMap[M, N, K, V, R], f) +} + +func MapRef[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(*V) R) func(M) N { + return F.Bind2nd(MonadMapRef[M, N, K, V, R], f) +} + +func MapWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(K, V) R) func(M) N { + return F.Bind2nd(MonadMapWithIndex[M, N, K, V, R], f) +} + +func MapRefWithIndex[M ~map[K]V, N ~map[K]R, K comparable, V, R any](f func(K, *V) R) func(M) N { + return F.Bind2nd(MonadMapRefWithIndex[M, N, K, V, R], f) +} + +func MonadLookup[M ~map[K]V, K comparable, V any](m M, k K) O.Option[V] { + if val, ok := m[k]; ok { + return O.Some(val) + } + return O.None[V]() +} + +func Lookup[M ~map[K]V, K comparable, V any](k K) func(M) O.Option[V] { + n := O.None[V]() + return func(m M) O.Option[V] { + if val, ok := m[k]; ok { + return O.Some(val) + } + return n + } +} + +func Has[M ~map[K]V, K comparable, V any](k K, r M) bool { + _, ok := r[k] + return ok +} + +func union[M ~map[K]V, K comparable, V any](m Mg.Magma[V], left M, right M) M { + lenLeft := len(left) + + if lenLeft == 0 { + return right + } + + lenRight := len(right) + if lenRight == 0 { + return left + } + + result := make(M, lenLeft+lenRight) + + for k, v := range left { + if val, ok := right[k]; ok { + result[k] = m.Concat(v, val) + } else { + result[k] = v + } + } + + for k, v := range right { + if _, ok := left[k]; !ok { + result[k] = v + } + } + + return result +} + +func unionLast[M ~map[K]V, K comparable, V any](left M, right M) M { + lenLeft := len(left) + + if lenLeft == 0 { + return right + } + + lenRight := len(right) + if lenRight == 0 { + return left + } + + result := make(M, lenLeft+lenRight) + + for k, v := range left { + result[k] = v + } + + for k, v := range right { + result[k] = v + } + + return result +} + +func Union[M ~map[K]V, K comparable, V any](m Mg.Magma[V]) func(M) func(M) M { + return func(right M) func(M) M { + return func(left M) M { + return union(m, left, right) + } + } +} + +func UnionLast[M ~map[K]V, K comparable, V any](right M) func(M) M { + return func(left M) M { + return unionLast(left, right) + } +} + +func Merge[M ~map[K]V, K comparable, V any](right M) func(M) M { + return UnionLast(right) +} + +func UnionFirst[M ~map[K]V, K comparable, V any](right M) func(M) M { + return func(left M) M { + return unionLast(right, left) + } +} + +func Empty[M ~map[K]V, K comparable, V any]() M { + return make(M) +} + +func Size[M ~map[K]V, K comparable, V any](r M) int { + return len(r) +} + +func ToArray[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT { + return collect[M, GT](r, T.MakeTuple2[K, V]) +} + +func toEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K], r M) GT { + // total number of elements + count := len(r) + // produce an array that we can sort by key + entries := make(GT, count) + idx := 0 + for k, v := range r { + entries[idx] = T.MakeTuple2(k, v) + idx++ + } + sort.Slice(entries, func(i, j int) bool { + return o.Compare(T.First(entries[i]), T.First(entries[j])) < 0 + }) + // final entries + return entries +} + +func ToEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K]) func(r M) GT { + return F.Bind1st(toEntriesOrd[M, GT, K, V], o) +} + +func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT { + return ToArray[M, GT](r) +} + +// FromFoldableMap uses the reduce method for a higher kinded type to transform +// its values into a tuple. The key and value are then used to populate the map. Duplicate +// values are resolved via the provided [Mg.Magma] +func FromFoldableMap[ + FCT ~func(A) T.Tuple2[K, V], + HKTA any, + FOLDABLE ~func(func(M, A) M, M) func(HKTA) M, + M ~map[K]V, + A any, + K comparable, + V any](m Mg.Magma[V], fld FOLDABLE) func(f FCT) func(fa HKTA) M { + return func(f FCT) func(fa HKTA) M { + return fld(func(dst M, a A) M { + if IsEmpty(dst) { + dst = make(M) + } + e := f(a) + k := T.First(e) + old, ok := dst[k] + if ok { + dst[k] = m.Concat(old, T.Second(e)) + } else { + dst[k] = T.Second(e) + } + return dst + }, Empty[M]()) + } +} + +func FromFoldable[ + HKTA any, + FOLDABLE ~func(func(M, T.Tuple2[K, V]) M, M) func(HKTA) M, + M ~map[K]V, + K comparable, + V any](m Mg.Magma[V], red FOLDABLE) func(fa HKTA) M { + return FromFoldableMap[func(T.Tuple2[K, V]) T.Tuple2[K, V], HKTA, FOLDABLE](m, red)(F.Identity[T.Tuple2[K, V]]) +} + +func FromArrayMap[ + FCT ~func(A) T.Tuple2[K, V], + GA ~[]A, + M ~map[K]V, + A any, + K comparable, + V any](m Mg.Magma[V]) func(f FCT) func(fa GA) M { + return FromFoldableMap[FCT](m, F.Bind23of3(RAG.Reduce[GA, A, M])) +} + +func FromArray[ + GA ~[]T.Tuple2[K, V], + M ~map[K]V, + K comparable, + V any](m Mg.Magma[V]) func(fa GA) M { + return FromFoldable[GA](m, F.Bind23of3(RAG.Reduce[GA, T.Tuple2[K, V], M])) +} + +func FromEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](fa GT) M { + m := make(M) + for _, t := range fa { + upsertAtReadWrite(m, t.F1, t.F2) + } + return m +} + +func duplicate[M ~map[K]V, K comparable, V any](r M) M { + return MonadMap[M, M](r, F.Identity[V]) +} + +func upsertAt[M ~map[K]V, K comparable, V any](r M, k K, v V) M { + dup := duplicate(r) + dup[k] = v + return dup +} + +func deleteAt[M ~map[K]V, K comparable, V any](r M, k K) M { + dup := duplicate(r) + delete(dup, k) + return dup +} + +func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M { + r[k] = v + return r +} + +func UpsertAt[M ~map[K]V, K comparable, V any](k K, v V) func(M) M { + return func(ma M) M { + return upsertAt(ma, k, v) + } +} + +func DeleteAt[M ~map[K]V, K comparable, V any](k K) func(M) M { + return F.Bind2nd(deleteAt[M, K, V], k) +} + +func Singleton[M ~map[K]V, K comparable, V any](k K, v V) M { + return M{k: v} +} + +func filterMapWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](fa M, f func(K, V1) O.Option[V2]) N { + return G.ReduceWithIndex(fa, func(key K, n N, value V1) N { + return O.MonadFold(f(key, value), F.Constant(n), func(v V2) N { + return upsertAtReadWrite(n, key, v) + }) + }, make(N)) +} + +func filterWithIndex[M ~map[K]V, K comparable, V any](fa M, f func(K, V) bool) M { + return filterMapWithIndex[M, M](fa, func(k K, v V) O.Option[V] { + if f(k, v) { + return O.Of(v) + } + return O.None[V]() + }) +} + +func filter[M ~map[K]V, K comparable, V any](fa M, f func(K) bool) M { + return filterWithIndex(fa, F.Ignore2of2[V](f)) +} + +// Filter creates a new map with only the elements that match the predicate +func Filter[M ~map[K]V, K comparable, V any](f func(K) bool) func(M) M { + return F.Bind2nd(filter[M, K, V], f) +} + +// FilterWithIndex creates a new map with only the elements that match the predicate +func FilterWithIndex[M ~map[K]V, K comparable, V any](f func(K, V) bool) func(M) M { + return F.Bind2nd(filterWithIndex[M, K, V], f) +} + +// FilterMapWithIndex creates a new map with only the elements for which the transformation function creates a Some +func FilterMapWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](f func(K, V1) O.Option[V2]) func(M) N { + return F.Bind2nd(filterMapWithIndex[M, N, K, V1, V2], f) +} + +// FilterMap creates a new map with only the elements for which the transformation function creates a Some +func FilterMap[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](f func(V1) O.Option[V2]) func(M) N { + return F.Bind2nd(filterMapWithIndex[M, N, K, V1, V2], F.Ignore1of2[K](f)) +} + +// Flatten converts a nested map into a regular map +func Flatten[M ~map[K]N, N ~map[K]V, K comparable, V any](m Mo.Monoid[N]) func(M) N { + return Chain[M, N](m)(F.Identity[N]) +} + +// FilterChainWithIndex creates a new map with only the elements for which the transformation function creates a Some +func FilterChainWithIndex[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N]) func(func(K, V1) O.Option[N]) func(M) N { + flatten := Flatten[map[K]N, N](m) + return func(f func(K, V1) O.Option[N]) func(M) N { + return F.Flow2( + FilterMapWithIndex[M, map[K]N](f), + flatten, + ) + } +} + +// FilterChain creates a new map with only the elements for which the transformation function creates a Some +func FilterChain[M ~map[K]V1, N ~map[K]V2, K comparable, V1, V2 any](m Mo.Monoid[N]) func(func(V1) O.Option[N]) func(M) N { + flatten := Flatten[map[K]N, N](m) + return func(f func(V1) O.Option[N]) func(M) N { + return F.Flow2( + FilterMap[M, map[K]N](f), + flatten, + ) + } +} + +// IsNil checks if the map is set to nil +func IsNil[M ~map[K]V, K comparable, V any](m M) bool { + return m == nil +} + +// IsNonNil checks if the map is set to nil +func IsNonNil[M ~map[K]V, K comparable, V any](m M) bool { + return m != nil +} + +// ConstNil return a nil map +func ConstNil[M ~map[K]V, K comparable, V any]() M { + return (M)(nil) +} + +func FoldMap[AS ~map[K]A, K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(AS) B { + return func(f func(A) B) func(AS) B { + return Reduce[AS](func(cur B, a A) B { + return m.Concat(cur, f(a)) + }, m.Empty()) + } +} + +func Fold[AS ~map[K]A, K comparable, A any](m Mo.Monoid[A]) func(AS) A { + return Reduce[AS](m.Concat, m.Empty()) +} + +func FoldMapWithIndex[AS ~map[K]A, K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(AS) B { + return func(f func(K, A) B) func(AS) B { + return ReduceWithIndex[AS](func(k K, cur B, a A) B { + return m.Concat(cur, f(k, a)) + }, m.Empty()) + } +} + +func ReduceOrdWithIndex[M ~map[K]V, K comparable, V, R any](o ord.Ord[K]) func(func(K, R, V) R, R) func(M) R { + return func(f func(K, R, V) R, initial R) func(M) R { + return func(m M) R { + return reduceOrd(o, m, f, initial) + } + } +} + +func ReduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K]) func(func(R, V) R, R) func(M) R { + ro := ReduceOrdWithIndex[M, K, V, R](o) + return func(f func(R, V) R, initial R) func(M) R { + return ro(F.Ignore1of3[K](f), initial) + } +} + +func FoldMapOrd[AS ~map[K]A, K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(A) B) func(AS) B { + red := ReduceOrd[AS, K, A, B](o) + return func(m Mo.Monoid[B]) func(func(A) B) func(AS) B { + return func(f func(A) B) func(AS) B { + return red(func(cur B, a A) B { + return m.Concat(cur, f(a)) + }, m.Empty()) + } + } +} + +func FoldOrd[AS ~map[K]A, K comparable, A any](o ord.Ord[K]) func(m Mo.Monoid[A]) func(AS) A { + red := ReduceOrd[AS, K, A, A](o) + return func(m Mo.Monoid[A]) func(AS) A { + return red(m.Concat, m.Empty()) + } +} + +func FoldMapOrdWithIndex[AS ~map[K]A, K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(K, A) B) func(AS) B { + red := ReduceOrdWithIndex[AS, K, A, B](o) + return func(m Mo.Monoid[B]) func(func(K, A) B) func(AS) B { + return func(f func(K, A) B) func(AS) B { + return red(func(k K, cur B, a A) B { + return m.Concat(cur, f(k, a)) + }, m.Empty()) + } + } +} + +func MonadFlap[GFAB ~map[K]func(A) B, GB ~map[K]B, K comparable, A, B any](fab GFAB, a A) GB { + return FC.MonadFlap(MonadMap[GFAB, GB], fab, a) +} + +func Flap[GFAB ~map[K]func(A) B, GB ~map[K]B, K comparable, A, B any](a A) func(GFAB) GB { + return FC.Flap(Map[GFAB, GB], a) +} + +func Copy[M ~map[K]V, K comparable, V any](m M) M { + return duplicate(m) +} + +func Clone[M ~map[K]V, K comparable, V any](f func(V) V) func(m M) M { + // impementation assumes that map does not optimize for the empty map + return Map[M, M](f) +} diff --git a/v2/record/generic/semigroup.go b/v2/record/generic/semigroup.go new file mode 100644 index 0000000..c1cf55d --- /dev/null +++ b/v2/record/generic/semigroup.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + S "github.com/IBM/fp-go/v2/semigroup" +) + +func UnionSemigroup[N ~map[K]V, K comparable, V any](s S.Semigroup[V]) S.Semigroup[N] { + return S.MakeSemigroup(func(first N, second N) N { + return union[N, K, V](S.ToMagma(s), first, second) + }) +} + +func UnionLastSemigroup[N ~map[K]V, K comparable, V any]() S.Semigroup[N] { + return S.MakeSemigroup(func(first N, second N) N { + return unionLast[N, K, V](first, second) + }) +} + +func UnionFirstSemigroup[N ~map[K]V, K comparable, V any]() S.Semigroup[N] { + return S.MakeSemigroup(func(first N, second N) N { + return unionLast[N, K, V](second, first) + }) +} diff --git a/v2/record/monoid.go b/v2/record/monoid.go new file mode 100644 index 0000000..84edb64 --- /dev/null +++ b/v2/record/monoid.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + M "github.com/IBM/fp-go/v2/monoid" + G "github.com/IBM/fp-go/v2/record/generic" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// UnionMonoid computes the union of two maps of the same type +func UnionMonoid[K comparable, V any](s S.Semigroup[V]) M.Monoid[map[K]V] { + return G.UnionMonoid[map[K]V](s) +} + +// UnionLastMonoid computes the union of two maps of the same type giving the last map precedence +func UnionLastMonoid[K comparable, V any]() M.Monoid[map[K]V] { + return G.UnionLastMonoid[map[K]V]() +} + +// UnionFirstMonoid computes the union of two maps of the same type giving the first map precedence +func UnionFirstMonoid[K comparable, V any]() M.Monoid[map[K]V] { + return G.UnionFirstMonoid[map[K]V]() +} + +// MergeMonoid computes the union of two maps of the same type giving the last map precedence +func MergeMonoid[K comparable, V any]() M.Monoid[map[K]V] { + return G.UnionLastMonoid[map[K]V]() +} diff --git a/v2/record/monoid_test.go b/v2/record/monoid_test.go new file mode 100644 index 0000000..4c888e5 --- /dev/null +++ b/v2/record/monoid_test.go @@ -0,0 +1,122 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + "testing" + + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +func TestUnionMonoid(t *testing.T) { + m := UnionMonoid[string](S.Semigroup()) + + e := Empty[string, string]() + + x := map[string]string{ + "a": "a1", + "b": "b1", + "c": "c1", + } + + y := map[string]string{ + "b": "b2", + "c": "c2", + "d": "d2", + } + + res := map[string]string{ + "a": "a1", + "b": "b1b2", + "c": "c1c2", + "d": "d2", + } + + assert.Equal(t, x, m.Concat(x, m.Empty())) + assert.Equal(t, x, m.Concat(m.Empty(), x)) + + assert.Equal(t, x, m.Concat(x, e)) + assert.Equal(t, x, m.Concat(e, x)) + + assert.Equal(t, res, m.Concat(x, y)) +} + +func TestUnionFirstMonoid(t *testing.T) { + m := UnionFirstMonoid[string, string]() + + e := Empty[string, string]() + + x := map[string]string{ + "a": "a1", + "b": "b1", + "c": "c1", + } + + y := map[string]string{ + "b": "b2", + "c": "c2", + "d": "d2", + } + + res := map[string]string{ + "a": "a1", + "b": "b1", + "c": "c1", + "d": "d2", + } + + assert.Equal(t, x, m.Concat(x, m.Empty())) + assert.Equal(t, x, m.Concat(m.Empty(), x)) + + assert.Equal(t, x, m.Concat(x, e)) + assert.Equal(t, x, m.Concat(e, x)) + + assert.Equal(t, res, m.Concat(x, y)) +} + +func TestUnionLastMonoid(t *testing.T) { + m := UnionLastMonoid[string, string]() + + e := Empty[string, string]() + + x := map[string]string{ + "a": "a1", + "b": "b1", + "c": "c1", + } + + y := map[string]string{ + "b": "b2", + "c": "c2", + "d": "d2", + } + + res := map[string]string{ + "a": "a1", + "b": "b2", + "c": "c2", + "d": "d2", + } + + assert.Equal(t, x, m.Concat(x, m.Empty())) + assert.Equal(t, x, m.Concat(m.Empty(), x)) + + assert.Equal(t, x, m.Concat(x, e)) + assert.Equal(t, x, m.Concat(e, x)) + + assert.Equal(t, res, m.Concat(x, y)) +} diff --git a/v2/record/record.go b/v2/record/record.go new file mode 100644 index 0000000..5290b0a --- /dev/null +++ b/v2/record/record.go @@ -0,0 +1,343 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + EM "github.com/IBM/fp-go/v2/endomorphism" + Mg "github.com/IBM/fp-go/v2/magma" + Mo "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/ord" + G "github.com/IBM/fp-go/v2/record/generic" + T "github.com/IBM/fp-go/v2/tuple" +) + +// IsEmpty tests if a map is empty +func IsEmpty[K comparable, V any](r map[K]V) bool { + return G.IsEmpty(r) +} + +// IsNonEmpty tests if a map is not empty +func IsNonEmpty[K comparable, V any](r map[K]V) bool { + return G.IsNonEmpty(r) +} + +// Keys returns the key in a map +func Keys[K comparable, V any](r map[K]V) []K { + return G.Keys[map[K]V, []K](r) +} + +// Values returns the values in a map +func Values[K comparable, V any](r map[K]V) []V { + return G.Values[map[K]V, []V](r) +} + +// Collect applies a collector function to the key value pairs in a map and returns the result as an array +func Collect[K comparable, V, R any](f func(K, V) R) func(map[K]V) []R { + return G.Collect[map[K]V, []R](f) +} + +// CollectOrd applies a collector function to the key value pairs in a map and returns the result as an array +func CollectOrd[V, R any, K comparable](o ord.Ord[K]) func(func(K, V) R) func(map[K]V) []R { + return G.CollectOrd[map[K]V, []R](o) +} + +func Reduce[K comparable, V, R any](f func(R, V) R, initial R) func(map[K]V) R { + return G.Reduce[map[K]V](f, initial) +} + +func ReduceWithIndex[K comparable, V, R any](f func(K, R, V) R, initial R) func(map[K]V) R { + return G.ReduceWithIndex[map[K]V](f, initial) +} + +func ReduceRef[K comparable, V, R any](f func(R, *V) R, initial R) func(map[K]V) R { + return G.ReduceRef[map[K]V](f, initial) +} + +func ReduceRefWithIndex[K comparable, V, R any](f func(K, R, *V) R, initial R) func(map[K]V) R { + return G.ReduceRefWithIndex[map[K]V](f, initial) +} + +func MonadMap[K comparable, V, R any](r map[K]V, f func(V) R) map[K]R { + return G.MonadMap[map[K]V, map[K]R](r, f) +} + +func MonadMapWithIndex[K comparable, V, R any](r map[K]V, f func(K, V) R) map[K]R { + return G.MonadMapWithIndex[map[K]V, map[K]R](r, f) +} + +func MonadMapRefWithIndex[K comparable, V, R any](r map[K]V, f func(K, *V) R) map[K]R { + return G.MonadMapRefWithIndex[map[K]V, map[K]R](r, f) +} + +func MonadMapRef[K comparable, V, R any](r map[K]V, f func(*V) R) map[K]R { + return G.MonadMapRef[map[K]V, map[K]R](r, f) +} + +func Map[K comparable, V, R any](f func(V) R) func(map[K]V) map[K]R { + return G.Map[map[K]V, map[K]R](f) +} + +func MapRef[K comparable, V, R any](f func(*V) R) func(map[K]V) map[K]R { + return G.MapRef[map[K]V, map[K]R](f) +} + +func MapWithIndex[K comparable, V, R any](f func(K, V) R) func(map[K]V) map[K]R { + return G.MapWithIndex[map[K]V, map[K]R](f) +} + +func MapRefWithIndex[K comparable, V, R any](f func(K, *V) R) func(map[K]V) map[K]R { + return G.MapRefWithIndex[map[K]V, map[K]R](f) +} + +// Lookup returns the entry for a key in a map if it exists +func Lookup[V any, K comparable](k K) func(map[K]V) O.Option[V] { + return G.Lookup[map[K]V](k) +} + +// MonadLookup returns the entry for a key in a map if it exists +func MonadLookup[V any, K comparable](m map[K]V, k K) O.Option[V] { + return G.MonadLookup[map[K]V](m, k) +} + +// Has tests if a key is contained in a map +func Has[K comparable, V any](k K, r map[K]V) bool { + return G.Has(k, r) +} + +func Union[K comparable, V any](m Mg.Magma[V]) func(map[K]V) func(map[K]V) map[K]V { + return G.Union[map[K]V](m) +} + +// Merge combines two maps giving the values in the right one precedence. Also refer to [MergeMonoid] +func Merge[K comparable, V any](right map[K]V) func(map[K]V) map[K]V { + return G.Merge[map[K]V](right) +} + +// Empty creates an empty map +func Empty[K comparable, V any]() map[K]V { + return G.Empty[map[K]V]() +} + +// Size returns the number of elements in a map +func Size[K comparable, V any](r map[K]V) int { + return G.Size(r) +} + +func ToArray[K comparable, V any](r map[K]V) []T.Tuple2[K, V] { + return G.ToArray[map[K]V, []T.Tuple2[K, V]](r) +} + +func ToEntries[K comparable, V any](r map[K]V) []T.Tuple2[K, V] { + return G.ToEntries[map[K]V, []T.Tuple2[K, V]](r) +} + +func FromEntries[K comparable, V any](fa []T.Tuple2[K, V]) map[K]V { + return G.FromEntries[map[K]V](fa) +} + +func UpsertAt[K comparable, V any](k K, v V) func(map[K]V) map[K]V { + return G.UpsertAt[map[K]V](k, v) +} + +func DeleteAt[K comparable, V any](k K) func(map[K]V) map[K]V { + return G.DeleteAt[map[K]V](k) +} + +// Singleton creates a new map with a single entry +func Singleton[K comparable, V any](k K, v V) map[K]V { + return G.Singleton[map[K]V](k, v) +} + +// FilterMapWithIndex creates a new map with only the elements for which the transformation function creates a Some +func FilterMapWithIndex[K comparable, V1, V2 any](f func(K, V1) O.Option[V2]) func(map[K]V1) map[K]V2 { + return G.FilterMapWithIndex[map[K]V1, map[K]V2](f) +} + +// FilterMap creates a new map with only the elements for which the transformation function creates a Some +func FilterMap[K comparable, V1, V2 any](f func(V1) O.Option[V2]) func(map[K]V1) map[K]V2 { + return G.FilterMap[map[K]V1, map[K]V2](f) +} + +// Filter creates a new map with only the elements that match the predicate +func Filter[K comparable, V any](f func(K) bool) func(map[K]V) map[K]V { + return G.Filter[map[K]V](f) +} + +// FilterWithIndex creates a new map with only the elements that match the predicate +func FilterWithIndex[K comparable, V any](f func(K, V) bool) func(map[K]V) map[K]V { + return G.FilterWithIndex[map[K]V](f) +} + +// IsNil checks if the map is set to nil +func IsNil[K comparable, V any](m map[K]V) bool { + return G.IsNil(m) +} + +// IsNonNil checks if the map is set to nil +func IsNonNil[K comparable, V any](m map[K]V) bool { + return G.IsNonNil(m) +} + +// ConstNil return a nil map +func ConstNil[K comparable, V any]() map[K]V { + return (map[K]V)(nil) +} + +func MonadChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2], r map[K]V1, f func(K, V1) map[K]V2) map[K]V2 { + return G.MonadChainWithIndex(m, r, f) +} + +func MonadChain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2], r map[K]V1, f func(V1) map[K]V2) map[K]V2 { + return G.MonadChain(m, r, f) +} + +func ChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(K, V1) map[K]V2) func(map[K]V1) map[K]V2 { + return G.ChainWithIndex[map[K]V1](m) +} + +func Chain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(V1) map[K]V2) func(map[K]V1) map[K]V2 { + return G.Chain[map[K]V1](m) +} + +// Flatten converts a nested map into a regular map +func Flatten[K comparable, V any](m Mo.Monoid[map[K]V]) func(map[K]map[K]V) map[K]V { + return G.Flatten[map[K]map[K]V](m) +} + +// FilterChainWithIndex creates a new map with only the elements for which the transformation function creates a Some +func FilterChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(K, V1) O.Option[map[K]V2]) func(map[K]V1) map[K]V2 { + return G.FilterChainWithIndex[map[K]V1](m) +} + +// FilterChain creates a new map with only the elements for which the transformation function creates a Some +func FilterChain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(V1) O.Option[map[K]V2]) func(map[K]V1) map[K]V2 { + return G.FilterChain[map[K]V1](m) +} + +// FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid. +func FoldMap[K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B { + return G.FoldMap[map[K]A](m) +} + +// FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid. +func FoldMapWithIndex[K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B { + return G.FoldMapWithIndex[map[K]A](m) +} + +// Fold folds the record using the provided Monoid. +func Fold[K comparable, A any](m Mo.Monoid[A]) func(map[K]A) A { + return G.Fold[map[K]A](m) +} + +// ReduceOrdWithIndex reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order +func ReduceOrdWithIndex[V, R any, K comparable](o ord.Ord[K]) func(func(K, R, V) R, R) func(map[K]V) R { + return G.ReduceOrdWithIndex[map[K]V, K, V, R](o) +} + +// ReduceOrd reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order +func ReduceOrd[V, R any, K comparable](o ord.Ord[K]) func(func(R, V) R, R) func(map[K]V) R { + return G.ReduceOrd[map[K]V, K, V, R](o) +} + +// FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order +func FoldMapOrd[A, B any, K comparable](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B { + return G.FoldMapOrd[map[K]A, K, A, B](o) +} + +// Fold folds the record using the provided Monoid with the items passed in the given order +func FoldOrd[A any, K comparable](o ord.Ord[K]) func(m Mo.Monoid[A]) func(map[K]A) A { + return G.FoldOrd[map[K]A, K, A](o) +} + +// FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order +func FoldMapOrdWithIndex[K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B { + return G.FoldMapOrdWithIndex[map[K]A, K, A, B](o) +} + +// KeysOrd returns the keys in the map in their given order +func KeysOrd[V any, K comparable](o ord.Ord[K]) func(r map[K]V) []K { + return G.KeysOrd[map[K]V, []K, K, V](o) +} + +// ValuesOrd returns the values in the map ordered by their keys in the given order +func ValuesOrd[V any, K comparable](o ord.Ord[K]) func(r map[K]V) []V { + return G.ValuesOrd[map[K]V, []V, K, V](o) +} + +func MonadFlap[B any, K comparable, A any](fab map[K]func(A) B, a A) map[K]B { + return G.MonadFlap[map[K]func(A) B, map[K]B](fab, a) +} + +func Flap[B any, K comparable, A any](a A) func(map[K]func(A) B) map[K]B { + return G.Flap[map[K]func(A) B, map[K]B](a) +} + +// Copy creates a shallow copy of the map +func Copy[K comparable, V any](m map[K]V) map[K]V { + return G.Copy[map[K]V](m) +} + +// Clone creates a deep copy of the map using the provided endomorphism to clone the values +func Clone[K comparable, V any](f EM.Endomorphism[V]) EM.Endomorphism[map[K]V] { + return G.Clone[map[K]V](f) +} + +// FromFoldableMap converts from a reducer to a map +// Duplicate keys are resolved by the provided [Mg.Magma] +func FromFoldableMap[ + FOLDABLE ~func(func(map[K]V, A) map[K]V, map[K]V) func(HKTA) map[K]V, // the reduce function + A any, + HKTA any, + K comparable, + V any](m Mg.Magma[V], red FOLDABLE) func(f func(A) T.Tuple2[K, V]) func(fa HKTA) map[K]V { + return G.FromFoldableMap[func(A) T.Tuple2[K, V]](m, red) +} + +// FromArrayMap converts from an array to a map +// Duplicate keys are resolved by the provided [Mg.Magma] +func FromArrayMap[ + A any, + K comparable, + V any](m Mg.Magma[V]) func(f func(A) T.Tuple2[K, V]) func(fa []A) map[K]V { + return G.FromArrayMap[func(A) T.Tuple2[K, V], []A, map[K]V](m) +} + +// FromFoldable converts from a reducer to a map +// Duplicate keys are resolved by the provided [Mg.Magma] +func FromFoldable[ + HKTA any, + FOLDABLE ~func(func(map[K]V, T.Tuple2[K, V]) map[K]V, map[K]V) func(HKTA) map[K]V, // the reduce function + K comparable, + V any](m Mg.Magma[V], red FOLDABLE) func(fa HKTA) map[K]V { + return G.FromFoldable[HKTA, FOLDABLE](m, red) +} + +// FromArray converts from an array to a map +// Duplicate keys are resolved by the provided [Mg.Magma] +func FromArray[ + K comparable, + V any](m Mg.Magma[V]) func(fa []T.Tuple2[K, V]) map[K]V { + return G.FromArray[[]T.Tuple2[K, V], map[K]V](m) +} + +func MonadAp[A any, K comparable, B any](m Mo.Monoid[map[K]B], fab map[K]func(A) B, fa map[K]A) map[K]B { + return G.MonadAp[map[K]B, map[K]func(A) B, map[K]A](m, fab, fa) +} + +func Ap[A any, K comparable, B any](m Mo.Monoid[map[K]B]) func(fa map[K]A) func(map[K]func(A) B) map[K]B { + return G.Ap[map[K]B, map[K]func(A) B, map[K]A](m) +} diff --git a/v2/record/record_test.go b/v2/record/record_test.go new file mode 100644 index 0000000..c2004ef --- /dev/null +++ b/v2/record/record_test.go @@ -0,0 +1,200 @@ +// All rights reserved. +// Copyright (c) 2023 IBM Corp. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + "fmt" + "sort" + "strings" + "testing" + + A "github.com/IBM/fp-go/v2/array" + "github.com/IBM/fp-go/v2/internal/utils" + Mg "github.com/IBM/fp-go/v2/magma" + O "github.com/IBM/fp-go/v2/option" + S "github.com/IBM/fp-go/v2/string" + T "github.com/IBM/fp-go/v2/tuple" + "github.com/stretchr/testify/assert" +) + +func TestKeys(t *testing.T) { + data := map[string]string{ + "a": "A", + "b": "B", + "c": "C", + } + keys := Keys(data) + sort.Strings(keys) + + assert.Equal(t, []string{"a", "b", "c"}, keys) +} + +func TestValues(t *testing.T) { + data := map[string]string{ + "a": "A", + "b": "B", + "c": "C", + } + keys := Values(data) + sort.Strings(keys) + + assert.Equal(t, []string{"A", "B", "C"}, keys) +} + +func TestMap(t *testing.T) { + data := map[string]string{ + "a": "a", + "b": "b", + "c": "c", + } + expected := map[string]string{ + "a": "A", + "b": "B", + "c": "C", + } + assert.Equal(t, expected, Map[string](utils.Upper)(data)) +} + +func TestLookup(t *testing.T) { + data := map[string]string{ + "a": "a", + "b": "b", + "c": "c", + } + assert.Equal(t, O.Some("a"), Lookup[string]("a")(data)) + assert.Equal(t, O.None[string](), Lookup[string]("a1")(data)) +} + +func TestFilterChain(t *testing.T) { + src := map[string]int{ + "a": 1, + "b": 2, + "c": 3, + } + + f := func(k string, value int) O.Option[map[string]string] { + if value%2 != 0 { + return O.Of(map[string]string{ + k: fmt.Sprintf("%s%d", k, value), + }) + } + return O.None[map[string]string]() + } + + // monoid + monoid := MergeMonoid[string, string]() + + res := FilterChainWithIndex[int](monoid)(f)(src) + + assert.Equal(t, map[string]string{ + "a": "a1", + "c": "c3", + }, res) +} + +func ExampleFoldMap() { + src := map[string]string{ + "a": "a", + "b": "b", + "c": "c", + } + + fold := FoldMapOrd[string, string](S.Ord)(S.Monoid)(strings.ToUpper) + + fmt.Println(fold(src)) + + // Output: ABC + +} + +func ExampleValuesOrd() { + src := map[string]string{ + "c": "a", + "b": "b", + "a": "c", + } + + getValues := ValuesOrd[string](S.Ord) + + fmt.Println(getValues(src)) + + // Output: [c b a] + +} + +func TestCopyVsClone(t *testing.T) { + slc := []string{"b", "c"} + src := map[string][]string{ + "a": slc, + } + // make a shallow copy + cpy := Copy(src) + // make a deep copy + cln := Clone[string](A.Copy[string])(src) + + assert.Equal(t, cpy, cln) + // make a modification to the original slice + slc[0] = "d" + assert.NotEqual(t, cpy, cln) + assert.Equal(t, src, cpy) +} + +func TestFromArrayMap(t *testing.T) { + src1 := A.From("a", "b", "c", "a") + frm := FromArrayMap[string, string](Mg.Second[string]()) + + f := frm(T.Replicate2[string]) + + res1 := f(src1) + + assert.Equal(t, map[string]string{ + "a": "a", + "b": "b", + "c": "c", + }, res1) + + src2 := A.From("A", "B", "C", "A") + + res2 := f(src2) + + assert.Equal(t, map[string]string{ + "A": "A", + "B": "B", + "C": "C", + }, res2) +} + +func TestEmpty(t *testing.T) { + nonEmpty := map[string]string{ + "a": "A", + "b": "B", + } + empty := Empty[string, string]() + + assert.True(t, IsEmpty(empty)) + assert.False(t, IsEmpty(nonEmpty)) + assert.False(t, IsNonEmpty(empty)) + assert.True(t, IsNonEmpty(nonEmpty)) +} + +func TestHas(t *testing.T) { + nonEmpty := map[string]string{ + "a": "A", + "b": "B", + } + assert.True(t, Has("a", nonEmpty)) + assert.False(t, Has("c", nonEmpty)) +} diff --git a/v2/record/semigroup.go b/v2/record/semigroup.go new file mode 100644 index 0000000..57332f3 --- /dev/null +++ b/v2/record/semigroup.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + G "github.com/IBM/fp-go/v2/record/generic" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func UnionSemigroup[K comparable, V any](s S.Semigroup[V]) S.Semigroup[map[K]V] { + return G.UnionSemigroup[map[K]V](s) +} + +func UnionLastSemigroup[K comparable, V any]() S.Semigroup[map[K]V] { + return G.UnionLastSemigroup[map[K]V]() +} + +func UnionFirstSemigroup[K comparable, V any]() S.Semigroup[map[K]V] { + return G.UnionFirstSemigroup[map[K]V]() +} diff --git a/v2/record/traverse.go b/v2/record/traverse.go new file mode 100644 index 0000000..1ca99e0 --- /dev/null +++ b/v2/record/traverse.go @@ -0,0 +1,53 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + G "github.com/IBM/fp-go/v2/internal/record" +) + +func TraverseWithIndex[K comparable, A, B, HKTB, HKTAB, HKTRB any]( + fof func(map[K]B) HKTRB, + fmap func(func(map[K]B) func(B) map[K]B) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + + f func(K, A) HKTB) func(map[K]A) HKTRB { + return G.TraverseWithIndex[map[K]A](fof, fmap, fap, f) +} + +// HKTA = HKT +// HKTB = HKT +// HKTAB = HKT +// HKTRB = HKT +func Traverse[K comparable, A, B, HKTB, HKTAB, HKTRB any]( + fof func(map[K]B) HKTRB, + fmap func(func(map[K]B) func(B) map[K]B) func(HKTRB) HKTAB, + fap func(HKTB) func(HKTAB) HKTRB, + f func(A) HKTB) func(map[K]A) HKTRB { + return G.Traverse[map[K]A](fof, fmap, fap, f) +} + +// HKTA = HKT[A] +// HKTAA = HKT[func(A)map[K]A] +// HKTRA = HKT[map[K]A] +func Sequence[K comparable, A, HKTA, HKTAA, HKTRA any]( + fof func(map[K]A) HKTRA, + fmap func(func(map[K]A) func(A) map[K]A) func(HKTRA) HKTAA, + fap func(HKTA) func(HKTAA) HKTRA, + ma map[K]HKTA) HKTRA { + return G.Sequence(fof, fmap, fap, ma) + +} diff --git a/v2/record/traverse_test.go b/v2/record/traverse_test.go new file mode 100644 index 0000000..bfc9456 --- /dev/null +++ b/v2/record/traverse_test.go @@ -0,0 +1,90 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package record + +import ( + "testing" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + "github.com/stretchr/testify/assert" +) + +type MapType = map[string]int +type MapTypeString = map[string]string +type MapTypeO = map[string]O.Option[int] + +func TestSimpleTraversalWithIndex(t *testing.T) { + + f := func(k string, n int) O.Option[int] { + if k != "a" { + return O.Some(n) + } + return O.None[int]() + } + + tWithIndex := TraverseWithIndex( + O.Of[MapType], + O.Map[MapType, func(int) MapType], + O.Ap[MapType, int], + f) + + assert.Equal(t, O.None[MapType](), F.Pipe1(MapType{"a": 1, "b": 2}, tWithIndex)) + assert.Equal(t, O.Some(MapType{"b": 2}), F.Pipe1(MapType{"b": 2}, tWithIndex)) +} + +func TestSimpleTraversalNoIndex(t *testing.T) { + + f := func(k string) O.Option[string] { + if k != "1" { + return O.Some(k) + } + return O.None[string]() + } + + tWithoutIndex := Traverse( + O.Of[MapTypeString], + O.Map[MapTypeString, func(string) MapTypeString], + O.Ap[MapTypeString, string], + f) + + assert.Equal(t, O.None[MapTypeString](), F.Pipe1(MapTypeString{"a": "1", "b": "2"}, tWithoutIndex)) + assert.Equal(t, O.Some(MapTypeString{"b": "2"}), F.Pipe1(MapTypeString{"b": "2"}, tWithoutIndex)) +} + +func TestSequence(t *testing.T) { + // source map + simpleMapO := MapTypeO{"a": O.Of(1), "b": O.Of(2)} + // convert to an option of record + + s := Traverse( + O.Of[MapType], + O.Map[MapType, func(int) MapType], + O.Ap[MapType, int], + F.Identity[O.Option[int]], + ) + + assert.Equal(t, O.Of(MapType{"a": 1, "b": 2}), F.Pipe1(simpleMapO, s)) + + s1 := Sequence( + O.Of[MapType], + O.Map[MapType, func(int) MapType], + O.Ap[MapType, int], + simpleMapO, + ) + + assert.Equal(t, O.Of(MapType{"a": 1, "b": 2}), s1) +} diff --git a/v2/reflect/generic/reflect.go b/v2/reflect/generic/reflect.go new file mode 100644 index 0000000..81b17ad --- /dev/null +++ b/v2/reflect/generic/reflect.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + R "reflect" +) + +func Map[GA ~[]A, A any](f func(R.Value) A) func(R.Value) GA { + return func(val R.Value) GA { + l := val.Len() + res := make(GA, l) + for i := l - 1; i >= 0; i-- { + res[i] = f(val.Index(i)) + } + return res + } +} diff --git a/v2/reflect/reflect.go b/v2/reflect/reflect.go new file mode 100644 index 0000000..97ca895 --- /dev/null +++ b/v2/reflect/reflect.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + R "reflect" + + F "github.com/IBM/fp-go/v2/function" + G "github.com/IBM/fp-go/v2/reflect/generic" +) + +func ReduceWithIndex[A any](f func(int, A, R.Value) A, initial A) func(R.Value) A { + return func(val R.Value) A { + count := val.Len() + current := initial + for i := 0; i < count; i++ { + current = f(i, current, val.Index(i)) + } + return current + } +} + +func Reduce[A any](f func(A, R.Value) A, initial A) func(R.Value) A { + return ReduceWithIndex(F.Ignore1of3[int](f), initial) +} + +func Map[A any](f func(R.Value) A) func(R.Value) []A { + return G.Map[[]A](f) +} diff --git a/v2/resources/images/logo.png b/v2/resources/images/logo.png new file mode 100644 index 0000000..f0242f5 Binary files /dev/null and b/v2/resources/images/logo.png differ diff --git a/v2/retry/generic/retry.go b/v2/retry/generic/retry.go new file mode 100644 index 0000000..08e9122 --- /dev/null +++ b/v2/retry/generic/retry.go @@ -0,0 +1,102 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "time" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + R "github.com/IBM/fp-go/v2/retry" +) + +// Apply policy and delay by its amount if it results in a R. +// Returns updated status. +// HKTSTATUS = HKT +func applyAndDelay[HKTSTATUS any]( + monadOf func(R.RetryStatus) HKTSTATUS, + monadDelay func(time.Duration) func(HKTSTATUS) HKTSTATUS, +) func(policy R.RetryPolicy, status R.RetryStatus) HKTSTATUS { + return func(policy R.RetryPolicy, status R.RetryStatus) HKTSTATUS { + newStatus := R.ApplyPolicy(policy, status) + return F.Pipe1( + newStatus.PreviousDelay, + O.Fold( + F.Nullary2(F.Constant(newStatus), monadOf), + func(delay time.Duration) HKTSTATUS { + return monadDelay(delay)(monadOf(newStatus)) + }, + ), + ) + } +} + +// Retry combinator for actions that don't raise exceptions, but +// signal in their type the outcome has failed. Examples are the +// `Option`, `Either` and `EitherT` monads. +// +// policy - refers to the retry policy +// action - converts a status into an operation to be executed +// check - checks if the result of the action needs to be retried +func Retrying[HKTA, HKTSTATUS, A any]( + monadChain func(func(A) HKTA) func(HKTA) HKTA, + monadChainStatus func(func(R.RetryStatus) HKTA) func(HKTSTATUS) HKTA, + monadOf func(A) HKTA, + monadOfStatus func(R.RetryStatus) HKTSTATUS, + monadDelay func(time.Duration) func(HKTSTATUS) HKTSTATUS, + + policy R.RetryPolicy, + action func(R.RetryStatus) HKTA, + check func(A) bool, +) HKTA { + // delay callback + applyDelay := applyAndDelay(monadOfStatus, monadDelay) + + // function to check if we need to retry or not + checkForRetry := O.FromPredicate(check) + + var f func(status R.RetryStatus) HKTA + + // need some lazy init because we reference it in the chain + f = func(status R.RetryStatus) HKTA { + return F.Pipe2( + status, + action, + monadChain(func(a A) HKTA { + return F.Pipe3( + a, + checkForRetry, + O.Map(func(a A) HKTA { + return F.Pipe1( + applyDelay(policy, status), + monadChainStatus(func(status R.RetryStatus) HKTA { + return F.Pipe1( + status.PreviousDelay, + O.Fold(F.Constant(monadOf(a)), func(_ time.Duration) HKTA { + return f(status) + }), + ) + }), + ) + }), + O.GetOrElse(F.Constant(monadOf(a))), + ) + }), + ) + } + // seed + return f(R.DefaultRetryStatus) +} diff --git a/v2/retry/retry.go b/v2/retry/retry.go new file mode 100644 index 0000000..1daacdb --- /dev/null +++ b/v2/retry/retry.go @@ -0,0 +1,121 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package retry + +import ( + "math" + "time" + + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/ord" +) + +type RetryStatus struct { + // Iteration number, where `0` is the first try + IterNumber uint + // Delay incurred so far from retries + CumulativeDelay time.Duration + // Latest attempt's delay. Will always be `none` on first run. + PreviousDelay O.Option[time.Duration] +} + +// RetryPolicy is a function that takes an `RetryStatus` and +// possibly returns a delay in milliseconds. Iteration numbers start +// at zero and increase by one on each retry. A //None// return value from +// the function implies we have reached the retry limit. +type RetryPolicy = func(RetryStatus) O.Option[time.Duration] + +const emptyDuration = time.Duration(0) + +var ordDuration = ord.FromStrictCompare[time.Duration]() + +// Monoid 'RetryPolicy' is a 'Monoid'. You can collapse multiple strategies into one using 'concat'. +// The semantics of this combination are as follows: +// +// 1. If either policy returns 'None', the combined policy returns +// 'None'. This can be used to inhibit after a number of retries, +// for example. +// +// 2. If both policies return a delay, the larger delay will be used. +// This is quite natural when combining multiple policies to achieve a +// certain effect. +var Monoid = M.FunctionMonoid[RetryStatus](O.ApplicativeMonoid(M.MakeMonoid( + ord.MaxSemigroup(ordDuration).Concat, emptyDuration))) + +// LimitRetries retries immediately, but only up to `i` times. +func LimitRetries(i uint) RetryPolicy { + pred := func(value uint) bool { + return value < i + } + empty := F.Constant1[uint](emptyDuration) + return func(status RetryStatus) O.Option[time.Duration] { + return F.Pipe2( + status.IterNumber, + O.FromPredicate(pred), + O.Map(empty), + ) + } +} + +// ConstantDelay delays with unlimited retries +func ConstantDelay(delay time.Duration) RetryPolicy { + return F.Constant1[RetryStatus](O.Of(delay)) +} + +// CapDelay sets a time-upperbound for any delays that may be directed by the +// given policy. This function does not terminate the retrying. The policy +// capDelay(maxDelay, exponentialBackoff(n))` will never stop retrying. It +// will reach a state where it retries forever with a delay of `maxDelay` +// between each one. To get termination you need to use one of the +// 'limitRetries' function variants. +func CapDelay(maxDelay time.Duration, policy RetryPolicy) RetryPolicy { + return F.Flow2( + policy, + O.Map(F.Bind1st(ord.Min(ordDuration), maxDelay)), + ) +} + +// ExponentialBackoff grows delay exponentially each iteration. +// Each delay will increase by a factor of two. +func ExponentialBackoff(delay time.Duration) RetryPolicy { + return func(status RetryStatus) O.Option[time.Duration] { + return O.Some(delay * time.Duration(math.Pow(2, float64(status.IterNumber)))) + } +} + +// DefaultRetryStatus is the default retry status. Exported mostly to allow user code +// to test their handlers and retry policies. +var DefaultRetryStatus = RetryStatus{ + IterNumber: 0, + CumulativeDelay: 0, + PreviousDelay: O.None[time.Duration](), +} + +var getOrElseDelay = O.GetOrElse(F.Constant(emptyDuration)) + +/** + * Apply policy on status to see what the decision would be. + */ +func ApplyPolicy(policy RetryPolicy, status RetryStatus) RetryStatus { + previousDelay := policy(status) + return RetryStatus{ + IterNumber: status.IterNumber + 1, + CumulativeDelay: status.CumulativeDelay + getOrElseDelay(previousDelay), + PreviousDelay: previousDelay, + } +} diff --git a/v2/samples/README.md b/v2/samples/README.md new file mode 100644 index 0000000..fdff004 --- /dev/null +++ b/v2/samples/README.md @@ -0,0 +1,14 @@ +# Samples + +This folder is meant to contain examples that illustrate how to use the library. I recommend the following reading to get an idea of the underlying concepts. These articles talk about [fp-ts](https://github.com/gcanti/fp-ts) but the concepts are very similar, only syntax differs. + +# Video Introduction + +[![introduction to fp-go](presentation/cover.jpg)](https://www.youtube.com/watch?v=Jif3jL6DRdw "introduction to fp-go") + +### References + +- [Ryan's Blog](https://rlee.dev/practical-guide-to-fp-ts-part-1) - practical introduction into FP concepts +- [Investigate Functional Programming Concepts in Go](https://betterprogramming.pub/investigate-functional-programming-concepts-in-go-1dada09bc913) - discussion around FP concepts in golang +- [Investigating the I/O Monad in Go](https://medium.com/better-programming/investigating-the-i-o-monad-in-go-3c0fabbb4b3d) - a closer look at I/O monads in golang +- \ No newline at end of file diff --git a/v2/samples/doc_test.go b/v2/samples/doc_test.go new file mode 100644 index 0000000..4c82791 --- /dev/null +++ b/v2/samples/doc_test.go @@ -0,0 +1,17 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package sample contains examples that illustrate how to use fp-go +package samples diff --git a/v2/samples/getting-started-with-fp-ts/reader/example1/reader_test.go b/v2/samples/getting-started-with-fp-ts/reader/example1/reader_test.go new file mode 100644 index 0000000..0d51272 --- /dev/null +++ b/v2/samples/getting-started-with-fp-ts/reader/example1/reader_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package example1 implements the first example in [https://dev.to/gcanti/getting-started-with-fp-ts-reader-1ie5] +package example1 + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" + I "github.com/IBM/fp-go/v2/number/integer" + "github.com/IBM/fp-go/v2/ord" + R "github.com/IBM/fp-go/v2/reader" + S "github.com/IBM/fp-go/v2/string" +) + +type ( + I18n struct { + True string + False string + } + + Dependencies struct { + I18n I18n + } +) + +var ( + // g: func(int) R.Reader[*Dependencies, string], note how the implementation does not depend on the dependencies + g = F.Flow2( + ord.Gt(I.Ord)(2), + f, + ) + + // h: func(string) R.Reader[*Dependencies, string], note how the implementation does not depend on the dependencies + h = F.Flow3( + S.Size, + N.Add(1), + g, + ) +) + +func f(b bool) R.Reader[*Dependencies, string] { + return func(deps *Dependencies) string { + if b { + return deps.I18n.True + } + return deps.I18n.False + } +} + +func ExampleReader() { + + deps := Dependencies{I18n: I18n{True: "vero", False: "falso"}} + + fmt.Println(h("foo")(&deps)) + fmt.Println(h("a")(&deps)) + + // Output: + // vero + // falso +} diff --git a/v2/samples/getting-started-with-fp-ts/reader/example2/reader_test.go b/v2/samples/getting-started-with-fp-ts/reader/example2/reader_test.go new file mode 100644 index 0000000..a249f8a --- /dev/null +++ b/v2/samples/getting-started-with-fp-ts/reader/example2/reader_test.go @@ -0,0 +1,97 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package example2 implements the second example in [https://dev.to/gcanti/getting-started-with-fp-ts-reader-1ie5] +package example2 + +import ( + "fmt" + + F "github.com/IBM/fp-go/v2/function" + ID "github.com/IBM/fp-go/v2/identity" + N "github.com/IBM/fp-go/v2/number" + I "github.com/IBM/fp-go/v2/number/integer" + "github.com/IBM/fp-go/v2/ord" + R "github.com/IBM/fp-go/v2/reader" + S "github.com/IBM/fp-go/v2/string" +) + +type ( + I18n struct { + True string + False string + } + + Dependencies struct { + I18n I18n + LowerBound int + } +) + +var ( + // h: func(string) R.Reader[*Dependencies, string], note how the implementation does not depend on the dependencies + h = F.Flow3( + S.Size, + N.Add(1), + g, + ) +) + +func getLowerBound(deps *Dependencies) int { + return deps.LowerBound +} + +func g(n int) R.Reader[*Dependencies, string] { + return F.Pipe1( + R.Ask[*Dependencies](), + R.Chain(F.Flow4( + getLowerBound, + ord.Gt(I.Ord), + ID.Flap[bool](n), + f, + )), + ) +} + +func f(b bool) R.Reader[*Dependencies, string] { + return func(deps *Dependencies) string { + if b { + return deps.I18n.True + } + return deps.I18n.False + } +} + +func ExampleReader() { + + deps1 := Dependencies{I18n: I18n{True: "vero", False: "falso"}, LowerBound: 2} + + hFoo := h("foo") + hA := h("a") + + fmt.Println(hFoo(&deps1)) + fmt.Println(hA(&deps1)) + + deps2 := Dependencies{I18n: I18n{True: "vero", False: "falso"}, LowerBound: 4} + + fmt.Println(hFoo(&deps2)) + fmt.Println(hA(&deps2)) + + // Output: + // vero + // falso + // falso + // falso +} diff --git a/v2/samples/http/http_test.go b/v2/samples/http/http_test.go new file mode 100644 index 0000000..0967005 --- /dev/null +++ b/v2/samples/http/http_test.go @@ -0,0 +1,113 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "context" + "fmt" + "testing" + + HTTP "net/http" + + A "github.com/IBM/fp-go/v2/array" + R "github.com/IBM/fp-go/v2/context/readerioeither" + H "github.com/IBM/fp-go/v2/context/readerioeither/http" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" + T "github.com/IBM/fp-go/v2/tuple" + "github.com/stretchr/testify/assert" +) + +type PostItem struct { + UserID uint `json:"userId"` + ID uint `json:"id"` + Title string `json:"title"` + Body string `json:"body"` +} + +type CatFact struct { + Fact string `json:"fact"` +} + +func idxToURL(idx int) string { + return fmt.Sprintf("https://jsonplaceholder.typicode.com/posts/%d", idx+1) +} + +// TestMultipleHttpRequests shows how to execute multiple HTTP requests in parallel assuming +// that the response structure of all requests is identical, which is why we can use [R.TraverseArray] +func TestMultipleHttpRequests(t *testing.T) { + // prepare the http client + client := H.MakeClient(HTTP.DefaultClient) + // readSinglePost sends a GET request and parses the response as [PostItem] + readSinglePost := H.ReadJSON[PostItem](client) + + // total number of http requests + count := 10 + + data := F.Pipe3( + A.MakeBy(count, idxToURL), + R.TraverseArray(F.Flow3( + H.MakeGetRequest, + readSinglePost, + R.ChainFirstIOK(IO.Logf[PostItem]("Log Single: %v")), + )), + R.ChainFirstIOK(IO.Logf[[]PostItem]("Log Result: %v")), + R.Map(A.Size[PostItem]), + ) + + result := data(context.Background()) + + assert.Equal(t, E.Of[error](count), result()) +} + +func heterogeneousHTTPRequests() R.ReaderIOEither[T.Tuple2[PostItem, CatFact]] { + // prepare the http client + client := H.MakeClient(HTTP.DefaultClient) + // readSinglePost sends a GET request and parses the response as [PostItem] + readSinglePost := H.ReadJSON[PostItem](client) + // readSingleCatFact sends a GET request and parses the response as [CatFact] + readSingleCatFact := H.ReadJSON[CatFact](client) + + return F.Pipe3( + T.MakeTuple2("https://jsonplaceholder.typicode.com/posts/1", "https://catfact.ninja/fact"), + T.Map2(H.MakeGetRequest, H.MakeGetRequest), + R.TraverseTuple2( + readSinglePost, + readSingleCatFact, + ), + R.ChainFirstIOK(IO.Logf[T.Tuple2[PostItem, CatFact]]("Log Result: %v")), + ) + +} + +// TestHeterogeneousHttpRequests shows how to execute multiple HTTP requests in parallel when +// the response structure of these requests is different. We use [R.TraverseTuple2] to account for the different types +func TestHeterogeneousHttpRequests(t *testing.T) { + data := heterogeneousHTTPRequests() + + result := data(context.Background()) + + fmt.Println(result()) +} + +// BenchmarkHeterogeneousHttpRequests shows how to execute multiple HTTP requests in parallel when +// the response structure of these requests is different. We use [R.TraverseTuple2] to account for the different types +func BenchmarkHeterogeneousHttpRequests(b *testing.B) { + for n := 0; n < b.N; n++ { + heterogeneousHTTPRequests()(context.Background())() + } +} diff --git a/v2/samples/match/examples_match_test.go b/v2/samples/match/examples_match_test.go new file mode 100644 index 0000000..f2afb17 --- /dev/null +++ b/v2/samples/match/examples_match_test.go @@ -0,0 +1,65 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package match + +import ( + "fmt" + + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + S "github.com/IBM/fp-go/v2/string" +) + +type Thing struct { + Name string +} + +func (t Thing) GetName() string { + return t.Name +} + +var ( + // func(Thing) Either[error, string] + getName = F.Flow2( + Thing.GetName, + E.FromPredicate(S.IsNonEmpty, errors.OnSome[string]("value [%s] is empty")), + ) + + // func(option.Option[Thing]) Either[error, string] + GetName = F.Flow2( + E.FromOption[Thing](errors.OnNone("value is none")), + E.Chain(getName), + ) +) + +func ExampleEither_match() { + + oThing := O.Of(Thing{"Carsten"}) + + res := F.Pipe2( + oThing, + GetName, + E.Fold(S.Format[error]("failed with error %v"), S.Format[string]("get value %s")), + ) + + fmt.Println(res) + + // Output: + // get value Carsten + +} diff --git a/v2/samples/mostly-adequate/README.md b/v2/samples/mostly-adequate/README.md new file mode 100644 index 0000000..caf7b84 --- /dev/null +++ b/v2/samples/mostly-adequate/README.md @@ -0,0 +1,6 @@ +# Mostly Adequate: fp-go Companion Guide + +This resource is meant to serve as a go "companion" resource to Professor [Frisby's Mostly Adequate Guide](https://github.com/MostlyAdequate/mostly-adequate-guide). + +It is a port of the [mostly-adequate-fp-ts](https://github.com/ChuckJonas/mostly-adequate-fp-ts/) book. + diff --git a/v2/samples/mostly-adequate/chapter01_test.go b/v2/samples/mostly-adequate/chapter01_test.go new file mode 100644 index 0000000..a604c6b --- /dev/null +++ b/v2/samples/mostly-adequate/chapter01_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import "fmt" + +type Flock struct { + Seagulls int +} + +func MakeFlock(n int) Flock { + return Flock{Seagulls: n} +} + +func (f *Flock) Conjoin(other *Flock) *Flock { + f.Seagulls += other.Seagulls + return f +} + +func (f *Flock) Breed(other *Flock) *Flock { + f.Seagulls = f.Seagulls * other.Seagulls + return f +} + +func Example_flock() { + + flockA := MakeFlock(4) + flockB := MakeFlock(2) + flockC := MakeFlock(0) + + fmt.Println(flockA.Conjoin(&flockC).Breed(&flockB).Conjoin(flockA.Breed(&flockB)).Seagulls) + + // Output: 32 +} diff --git a/v2/samples/mostly-adequate/chapter02_firstclassfunctions_test.go b/v2/samples/mostly-adequate/chapter02_firstclassfunctions_test.go new file mode 100644 index 0000000..9b3ab7e --- /dev/null +++ b/v2/samples/mostly-adequate/chapter02_firstclassfunctions_test.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import "fmt" + +func Hi(name string) string { + return fmt.Sprintf("Hi %s", name) +} + +func Greeting(name string) string { + return Hi(name) +} + +func Example_greeting() { + // functions are first class objects + greet := Hi + + fmt.Println(Greeting("times")) + fmt.Println(greet("times")) + + // Output: + // Hi times + // Hi times +} diff --git a/v2/samples/mostly-adequate/chapter04_currying_test.go b/v2/samples/mostly-adequate/chapter04_currying_test.go new file mode 100644 index 0000000..b672e42 --- /dev/null +++ b/v2/samples/mostly-adequate/chapter04_currying_test.go @@ -0,0 +1,83 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + "fmt" + "math" + "regexp" + "strings" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" + I "github.com/IBM/fp-go/v2/number/integer" + S "github.com/IBM/fp-go/v2/string" +) + +var ( + Match = F.Curry2((*regexp.Regexp).FindStringSubmatch) + Matches = F.Curry2((*regexp.Regexp).MatchString) + Split = F.Curry2(F.Bind3of3((*regexp.Regexp).Split)(-1)) + + Add = N.Add[int] + ToString = I.ToString + ToLower = strings.ToLower + ToUpper = strings.ToUpper + Concat = F.Curry2(S.Monoid.Concat) +) + +// Replace cannot be generated via [F.Curry3] because the order of parameters does not match our desired curried order +func Replace(search *regexp.Regexp) func(replace string) func(s string) string { + return func(replace string) func(s string) string { + return func(s string) string { + return search.ReplaceAllString(s, replace) + } + } +} + +func Example_solution04A() { + // words :: String -> [String] + words := Split(regexp.MustCompile(` `)) + + fmt.Println(words("Jingle bells Batman smells")) + + // Output: + // [Jingle bells Batman smells] +} + +func Example_solution04B() { + // filterQs :: [String] -> [String] + filterQs := A.Filter(Matches(regexp.MustCompile(`q`))) + + fmt.Println(filterQs(A.From("quick", "camels", "quarry", "over", "quails"))) + + // Output: + // [quick quarry quails] +} + +func Example_solution04C() { + + keepHighest := N.Max[int] + + // max :: [Number] -> Number + max := A.Reduce(keepHighest, math.MinInt) + + fmt.Println(max(A.From(323, 523, 554, 123, 5234))) + + // Output: + // 5234 +} diff --git a/v2/samples/mostly-adequate/chapter05_composing_test.go b/v2/samples/mostly-adequate/chapter05_composing_test.go new file mode 100644 index 0000000..aa5c4d3 --- /dev/null +++ b/v2/samples/mostly-adequate/chapter05_composing_test.go @@ -0,0 +1,110 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + "fmt" + "regexp" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/number/integer" + O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/ord" + S "github.com/IBM/fp-go/v2/string" +) + +var ( + Exclaim = S.Format[string]("%s!") + Shout = F.Flow2(ToUpper, Exclaim) + Dasherize = F.Flow4( + Replace(regexp.MustCompile(`\s{2,}`))(" "), + Split(regexp.MustCompile(` `)), + A.Map(ToLower), + A.Intercalate(S.Monoid)("-"), + ) +) + +func Example_shout() { + fmt.Println(Shout("send in the clowns")) + + // Output: SEND IN THE CLOWNS! +} + +func Example_dasherize() { + fmt.Println(Dasherize("The world is a vampire")) + + // Output: the-world-is-a-vampire +} + +func Example_pipe() { + output := F.Pipe2( + "send in the clowns", + ToUpper, + Exclaim, + ) + + fmt.Println(output) + + // Output: SEND IN THE CLOWNS! +} + +func Example_solution05A() { + IsLastInStock := F.Flow2( + A.Last[Car], + O.Map(Car.getInStock), + ) + + fmt.Println(IsLastInStock(Cars[0:3])) + fmt.Println(IsLastInStock(Cars[3:])) + + // Output: + // Some[bool](true) + // Some[bool](false) +} + +func Example_solution05B() { + // averageDollarValue :: [Car] -> Int + averageDollarValue := F.Flow2( + A.Map(Car.getDollarValue), + average, + ) + + fmt.Println(averageDollarValue(Cars)) + + // Output: + // 790700 +} + +func Example_solution05C() { + // order by horsepower + ordByHorsepower := ord.Contramap(Car.getHorsepower)(I.Ord) + + // fastestCar :: [Car] -> Option[String] + fastestCar := F.Flow3( + A.Sort(ordByHorsepower), + A.Last[Car], + O.Map(F.Flow2( + Car.getName, + S.Format[string]("%s is the fastest"), + )), + ) + + fmt.Println(fastestCar(Cars)) + + // Output: + // Some[string](Aston Martin One-77 is the fastest) +} diff --git a/v2/samples/mostly-adequate/chapter06_exampleapplication_test.go b/v2/samples/mostly-adequate/chapter06_exampleapplication_test.go new file mode 100644 index 0000000..593d8b2 --- /dev/null +++ b/v2/samples/mostly-adequate/chapter06_exampleapplication_test.go @@ -0,0 +1,115 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + "context" + "fmt" + "net/http" + "regexp" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + J "github.com/IBM/fp-go/v2/json" + S "github.com/IBM/fp-go/v2/string" + + R "github.com/IBM/fp-go/v2/context/readerioeither" + H "github.com/IBM/fp-go/v2/context/readerioeither/http" +) + +type ( + FlickrMedia struct { + Link string `json:"m"` + } + + FlickrItem struct { + Media FlickrMedia `json:"media"` + } + + FlickrFeed struct { + Items []FlickrItem `json:"items"` + } +) + +func (f FlickrMedia) getLink() string { + return f.Link +} + +func (f FlickrItem) getMedia() FlickrMedia { + return f.Media +} + +func (f FlickrFeed) getItems() []FlickrItem { + return f.Items +} + +func Example_application() { + // pure + host := "api.flickr.com" + path := "/services/feeds/photos_public.gne" + query := S.Format[string]("?tags=%s&format=json&jsoncallback=?") + url := F.Flow2( + query, + S.Format[string](fmt.Sprintf("https://%s%s%%s", host, path)), + ) + // flick returns jsonP, we extract the JSON body, this is handled by jquery in the original code + sanitizeJSONP := Replace(regexp.MustCompile(`(?s)^\s*\((.*)\)\s*$`))("$1") + // parse jsonP + parseJSONP := F.Flow3( + sanitizeJSONP, + S.ToBytes, + J.Unmarshal[FlickrFeed], + ) + // markup + img := S.Format[string]("") + // lenses + mediaURL := F.Flow2( + FlickrItem.getMedia, + FlickrMedia.getLink, + ) + mediaURLs := F.Flow2( + FlickrFeed.getItems, + A.Map(mediaURL), + ) + images := F.Flow2( + mediaURLs, + A.Map(img), + ) + + client := H.MakeClient(http.DefaultClient) + + // func(string) R.ReaderIOEither[[]string] + app := F.Flow5( + url, + H.MakeGetRequest, + H.ReadText(client), + R.ChainEitherK(parseJSONP), + R.Map(images), + ) + + // R.ReaderIOEither[[]string] + // this is the managed effect that can be called to download and render the images + catImageEffect := app("cats") + + // impure, actually executes the effect + catImages := catImageEffect(context.TODO())() + fmt.Println(E.IsRight(catImages)) + + // Output: + // true + +} diff --git a/v2/samples/mostly-adequate/chapter08_tupperware_test.go b/v2/samples/mostly-adequate/chapter08_tupperware_test.go new file mode 100644 index 0000000..e11d9b3 --- /dev/null +++ b/v2/samples/mostly-adequate/chapter08_tupperware_test.go @@ -0,0 +1,276 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + "fmt" + "time" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity" + IOE "github.com/IBM/fp-go/v2/ioeither" + N "github.com/IBM/fp-go/v2/number" + O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/ord" + S "github.com/IBM/fp-go/v2/string" +) + +type Account struct { + Balance float32 +} + +func MakeAccount(b float32) Account { + return Account{Balance: b} +} + +func getBalance(a Account) float32 { + return a.Balance +} + +type ( + Chapter08User struct { + Id int + Name string + Active bool + Saved bool + } +) + +var ( + albert08 = Chapter08User{ + Id: 1, + Active: true, + Name: "Albert", + } + + gary08 = Chapter08User{ + Id: 2, + Active: false, + Name: "Gary", + } + + theresa08 = Chapter08User{ + Id: 3, + Active: true, + Name: "Theresa", + } + + yi08 = Chapter08User{Id: 4, Name: "Yi", Active: true} +) + +func (u Chapter08User) getName() string { + return u.Name +} + +func (u Chapter08User) isActive() bool { + return u.Active +} + +var ( + ordFloat32 = ord.FromStrictCompare[float32]() + UpdateLedger = F.Identity[Account] + RemainingBalance = F.Flow2( + getBalance, + S.Format[float32]("Your balance is $%0.2f"), + ) + FinishTransaction = F.Flow2( + UpdateLedger, + RemainingBalance, + ) + getTwenty = F.Flow2( + Withdraw(20), + O.Fold(F.Constant("You're broke!"), FinishTransaction), + ) + + // showWelcome :: User -> String + showWelcome = F.Flow2( + Chapter08User.getName, + S.Format[string]("Welcome %s"), + ) + + // checkActive :: User -> Either error User + checkActive = E.FromPredicate(Chapter08User.isActive, F.Constant1[Chapter08User](fmt.Errorf("your account is not active"))) + + // validateUser :: (User -> Either String ()) -> User -> Either String User + validateUser = F.Curry2(func(validate func(Chapter08User) E.Either[error, any], user Chapter08User) E.Either[error, Chapter08User] { + return F.Pipe2( + user, + validate, + E.MapTo[error, any](user), + ) + }) + + // save :: User -> IOEither error User + save = func(user Chapter08User) IOE.IOEither[error, Chapter08User] { + return IOE.FromIO[error](func() Chapter08User { + var u = user + u.Saved = true + return u + }) + } +) + +func Withdraw(amount float32) func(account Account) O.Option[Account] { + + return F.Flow3( + getBalance, + O.FromPredicate(ord.Geq(ordFloat32)(amount)), + O.Map(F.Flow2( + N.Add(-amount), + MakeAccount, + ))) +} + +type User struct { + BirthDate string +} + +func getBirthDate(u User) string { + return u.BirthDate +} + +func MakeUser(d string) User { + return User{BirthDate: d} +} + +var parseDate = F.Bind1of2(E.Eitherize2(time.Parse))(time.DateOnly) + +func GetAge(now time.Time) func(User) E.Either[error, float64] { + return F.Flow3( + getBirthDate, + parseDate, + E.Map[error](F.Flow3( + now.Sub, + time.Duration.Hours, + N.Mul(1/24.0), + )), + ) +} + +func Example_widthdraw() { + fmt.Println(getTwenty(MakeAccount(200))) + fmt.Println(getTwenty(MakeAccount(10))) + + // Output: + // Your balance is $180.00 + // You're broke! +} + +func Example_getAge() { + now, err := time.Parse(time.DateOnly, "2023-09-01") + if err != nil { + panic(err) + } + + fmt.Println(GetAge(now)(MakeUser("2005-12-12"))) + fmt.Println(GetAge(now)(MakeUser("July 4, 2001"))) + + fortune := F.Flow3( + N.Add(365.0), + S.Format[float64]("%0.0f"), + Concat("If you survive, you will be "), + ) + + zoltar := F.Flow3( + GetAge(now), + E.Map[error](fortune), + E.GetOrElse(errors.ToString), + ) + + fmt.Println(zoltar(MakeUser("2005-12-12"))) + + // Output: + // Right[float64](6472) + // Left[*time.ParseError](parsing time "July 4, 2001" as "2006-01-02": cannot parse "July 4, 2001" as "2006") + // If you survive, you will be 6837 +} + +func Example_solution08A() { + incrF := I.Map(N.Add(1)) + + fmt.Println(incrF(I.Of(2))) + + // Output: 3 +} + +func Example_solution08B() { + // initial :: User -> Option rune + initial := F.Flow3( + Chapter08User.getName, + S.ToRunes, + A.Head[rune], + ) + + fmt.Println(initial(albert08)) + + // Output: + // Some[int32](65) +} + +func Example_solution08C() { + + // eitherWelcome :: User -> Either String String + eitherWelcome := F.Flow2( + checkActive, + E.Map[error](showWelcome), + ) + + fmt.Println(eitherWelcome(gary08)) + fmt.Println(eitherWelcome(theresa08)) + + // Output: + // Left[*errors.errorString](your account is not active) + // Right[string](Welcome Theresa) +} + +func Example_solution08D() { + + // // validateName :: User -> Either String () + validateName := F.Flow3( + Chapter08User.getName, + E.FromPredicate(F.Flow2( + S.Size, + ord.Gt(ord.FromStrictCompare[int]())(3), + ), errors.OnSome[string]("Your name %s is larger than 3 characters")), + E.Map[error](F.ToAny[string]), + ) + + saveAndWelcome := F.Flow2( + save, + IOE.Map[error](showWelcome), + ) + + register := F.Flow3( + validateUser(validateName), + IOE.FromEither[error, Chapter08User], + IOE.Chain(saveAndWelcome), + ) + + fmt.Println(validateName(gary08)) + fmt.Println(validateName(yi08)) + + fmt.Println(register(albert08)()) + fmt.Println(register(yi08)()) + + // Output: + // Right[string](Gary) + // Left[*errors.errorString](Your name Yi is larger than 3 characters) + // Right[string](Welcome Albert) + // Left[*errors.errorString](Your name Yi is larger than 3 characters) +} diff --git a/v2/samples/mostly-adequate/chapter09_monadiconions_test.go b/v2/samples/mostly-adequate/chapter09_monadiconions_test.go new file mode 100644 index 0000000..42c23f9 --- /dev/null +++ b/v2/samples/mostly-adequate/chapter09_monadiconions_test.go @@ -0,0 +1,186 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + "fmt" + "path" + "regexp" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/io" + IOE "github.com/IBM/fp-go/v2/ioeither" + O "github.com/IBM/fp-go/v2/option" + S "github.com/IBM/fp-go/v2/string" +) + +type ( + Street struct { + Name string + Number int + } + + Address struct { + Street Street + Postcode string + } + + AddressBook struct { + Addresses []Address + } + + Chapter09User struct { + Id int + Name string + Address Address + } +) + +var ( + albert09 = Chapter09User{ + Id: 1, + Name: "Albert", + Address: Address{ + Street: Street{ + Number: 22, + Name: "Walnut St", + }, + }, + } + + gary09 = Chapter09User{ + Id: 2, + Name: "Gary", + Address: Address{ + Street: Street{ + Number: 14, + }, + }, + } + + theresa09 = Chapter09User{ + Id: 3, + Name: "Theresa", + } +) + +func (ab AddressBook) getAddresses() []Address { + return ab.Addresses +} + +func (s Address) getStreet() Street { + return s.Street +} + +func (s Street) getName() string { + return s.Name +} + +func (u Chapter09User) getAddress() Address { + return u.Address +} + +var ( + FirstAddressStreet = F.Flow3( + AddressBook.getAddresses, + A.Head[Address], + O.Map(Address.getStreet), + ) + + // getFile :: IO String + getFile = io.Of("/home/mostly-adequate/ch09.md") + + // pureLog :: String -> IO () + pureLog = io.Logf[string]("%s") + + // addToMailingList :: Email -> IOEither([Email]) + addToMailingList = F.Flow2( + A.Of[string], + IOE.Of[error, []string], + ) + + // validateEmail :: Email -> Either error Email + validateEmail = E.FromPredicate(Matches(regexp.MustCompile(`\S+@\S+\.\S+`)), errors.OnSome[string]("email %s is invalid")) + + // emailBlast :: [Email] -> IO () + emailBlast = F.Flow2( + A.Intercalate(S.Monoid)(","), + IOE.Of[error, string], + ) +) + +func Example_street() { + s := FirstAddressStreet(AddressBook{ + Addresses: A.From(Address{Street: Street{Name: "Mulburry", Number: 8402}, Postcode: "WC2N"}), + }) + fmt.Println(s) + + // Output: + // Some[mostlyadequate.Street]({Mulburry 8402}) +} + +func Example_solution09A() { + // // getStreetName :: User -> Maybe String + getStreetName := F.Flow4( + Chapter09User.getAddress, + Address.getStreet, + Street.getName, + O.FromPredicate(S.IsNonEmpty), + ) + + fmt.Println(getStreetName(albert09)) + fmt.Println(getStreetName(gary09)) + fmt.Println(getStreetName(theresa09)) + + // Output: + // Some[string](Walnut St) + // None[string] + // None[string] + +} + +func Example_solution09B() { + logFilename := F.Flow2( + io.Map(path.Base), + io.ChainFirst(pureLog), + ) + + fmt.Println(logFilename(getFile)()) + + // Output: + // ch09.md +} + +func Example_solution09C() { + + // // joinMailingList :: Email -> Either String (IO ()) + joinMailingList := F.Flow4( + validateEmail, + IOE.FromEither[error, string], + IOE.Chain(addToMailingList), + IOE.Chain(emailBlast), + ) + + fmt.Println(joinMailingList("sleepy@grandpa.net")()) + fmt.Println(joinMailingList("notanemail")()) + + // Output: + // Right[string](sleepy@grandpa.net) + // Left[*errors.errorString](email notanemail is invalid) +} diff --git a/v2/samples/mostly-adequate/chapter10_applicativefunctor_test.go b/v2/samples/mostly-adequate/chapter10_applicativefunctor_test.go new file mode 100644 index 0000000..24fcb3a --- /dev/null +++ b/v2/samples/mostly-adequate/chapter10_applicativefunctor_test.go @@ -0,0 +1,183 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + "context" + "fmt" + "net/http" + + R "github.com/IBM/fp-go/v2/context/readerioeither" + H "github.com/IBM/fp-go/v2/context/readerioeither/http" + F "github.com/IBM/fp-go/v2/function" + IOO "github.com/IBM/fp-go/v2/iooption" + N "github.com/IBM/fp-go/v2/number" + O "github.com/IBM/fp-go/v2/option" + M "github.com/IBM/fp-go/v2/record" + T "github.com/IBM/fp-go/v2/tuple" +) + +type ( + PostItem struct { + UserID uint `json:"userId"` + Id uint `json:"id"` + Title string `json:"title"` + Body string `json:"body"` + } + + Player struct { + Id int + Name string + } + + LocalStorage = map[string]Player +) + +var ( + playerAlbert = Player{ + Id: 1, + Name: "Albert", + } + playerTheresa = Player{ + Id: 2, + Name: "Theresa", + } + localStorage = LocalStorage{ + "player1": playerAlbert, + "player2": playerTheresa, + } + + // getFromCache :: String -> IO User + getFromCache = func(name string) IOO.IOOption[Player] { + return func() O.Option[Player] { + return M.MonadLookup(localStorage, name) + } + } + + // game :: User -> User -> String + game = F.Curry2(func(a, b Player) string { + return fmt.Sprintf("%s vs %s", a.Name, b.Name) + }) +) + +func (player Player) getName() string { + return player.Name +} + +func (player Player) getID() int { + return player.Id +} + +func (item PostItem) getTitle() string { + return item.Title +} + +func idxToURL(idx int) string { + return fmt.Sprintf("https://jsonplaceholder.typicode.com/posts/%d", idx+1) +} + +func renderString(destinations string) func(string) string { + return func(events string) string { + return fmt.Sprintf("
Destinations: [%s], Events: [%s]
", destinations, events) + } +} + +func Example_renderPage() { + // prepare the http client + client := H.MakeClient(http.DefaultClient) + + // get returns the title of the nth item from the REST service + get := F.Flow4( + idxToURL, + H.MakeGetRequest, + H.ReadJSON[PostItem](client), + R.Map(PostItem.getTitle), + ) + + res := F.Pipe2( + R.Of(renderString), // start with a function with 2 unresolved arguments + R.Ap[func(string) string](get(1)), // resolve the first argument + R.Ap[string](get(2)), // in parallel resolve the second argument + ) + + // finally invoke in context and start + fmt.Println(res(context.TODO())()) + + // Output: + // Right[string](
Destinations: [qui est esse], Events: [ea molestias quasi exercitationem repellat qui ipsa sit aut]
) + +} + +func Example_solution10A() { + safeAdd := F.Curry2(func(a, b O.Option[int]) O.Option[int] { + return F.Pipe3( + N.Add[int], + O.Of[func(int) func(int) int], + O.Ap[func(int) int](a), + O.Ap[int](b), + ) + }) + + fmt.Println(safeAdd(O.Of(2))(O.Of(3))) + fmt.Println(safeAdd(O.None[int]())(O.Of(3))) + fmt.Println(safeAdd(O.Of(2))(O.None[int]())) + + // Output: + // Some[int](5) + // None[int] + // None[int] +} + +func Example_solution10B() { + + safeAdd := F.Curry2(T.Untupled2(F.Flow2( + O.SequenceTuple2[int, int], + O.Map(T.Tupled2(N.MonoidSum[int]().Concat)), + ))) + + fmt.Println(safeAdd(O.Of(2))(O.Of(3))) + fmt.Println(safeAdd(O.None[int]())(O.Of(3))) + fmt.Println(safeAdd(O.Of(2))(O.None[int]())) + + // Output: + // Some[int](5) + // None[int] + // None[int] +} + +func Example_solution10C() { + // startGame :: IO String + startGame := F.Pipe2( + IOO.Of(game), + IOO.Ap[func(Player) string](getFromCache("player1")), + IOO.Ap[string](getFromCache("player2")), + ) + + startGameTupled := F.Pipe2( + T.MakeTuple2("player1", "player2"), + IOO.TraverseTuple2(getFromCache, getFromCache), + IOO.Map(T.Tupled2(func(a, b Player) string { + return fmt.Sprintf("%s vs %s", a.Name, b.Name) + })), + ) + + fmt.Println(startGame()) + fmt.Println(startGameTupled()) + + // Output: + // Some[string](Albert vs Theresa) + // Some[string](Albert vs Theresa) +} diff --git a/v2/samples/mostly-adequate/chapter11_transformagain_test.go b/v2/samples/mostly-adequate/chapter11_transformagain_test.go new file mode 100644 index 0000000..b8db081 --- /dev/null +++ b/v2/samples/mostly-adequate/chapter11_transformagain_test.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + "fmt" + "regexp" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + S "github.com/IBM/fp-go/v2/string" +) + +func findUserByID(id int) IOE.IOEither[error, Chapter08User] { + switch id { + case 1: + return IOE.Of[error](albert08) + case 2: + return IOE.Of[error](gary08) + case 3: + return IOE.Of[error](theresa08) + default: + return IOE.Left[Chapter08User](fmt.Errorf("user %d not found", id)) + } +} + +func Example_solution11A() { + // eitherToMaybe :: Either b a -> Maybe a + eitherToMaybe := E.ToOption[error, string] + + fmt.Println(eitherToMaybe(E.Of[error]("one eyed willy"))) + fmt.Println(eitherToMaybe(E.Left[string](fmt.Errorf("some error")))) + + // Output: + // Some[string](one eyed willy) + // None[string] +} + +func Example_solution11B() { + findByNameID := F.Flow2( + findUserByID, + IOE.Map[error](Chapter08User.getName), + ) + + fmt.Println(findByNameID(1)()) + fmt.Println(findByNameID(2)()) + fmt.Println(findByNameID(3)()) + fmt.Println(findByNameID(4)()) + + // Output: + // Right[string](Albert) + // Right[string](Gary) + // Right[string](Theresa) + // Left[*errors.errorString](user 4 not found) +} + +func Example_solution11C() { + // strToList :: String -> [Char + strToList := Split(regexp.MustCompile(``)) + + // listToStr :: [Char] -> String + listToStr := A.Intercalate(S.Monoid)("") + + sortLetters := F.Flow3( + strToList, + A.Sort(S.Ord), + listToStr, + ) + + fmt.Println(sortLetters("sortme")) + + // Output: + // emorst +} diff --git a/v2/samples/mostly-adequate/chapter12_traversing_test.go b/v2/samples/mostly-adequate/chapter12_traversing_test.go new file mode 100644 index 0000000..6f19822 --- /dev/null +++ b/v2/samples/mostly-adequate/chapter12_traversing_test.go @@ -0,0 +1,98 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + "fmt" + + A "github.com/IBM/fp-go/v2/array" + E "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/errors" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/predicate" + S "github.com/IBM/fp-go/v2/string" +) + +var ( + // httpGet :: Route -> Task Error JSON + httpGet = F.Flow2( + S.Format[string]("json for %s"), + IOE.Of[error, string], + ) + + // routes :: Map Route Route + routes = map[string]string{ + "/": "/", + "/about": "/about", + } + + // validate :: Player -> Either error Player + validatePlayer = E.FromPredicate(P.ContraMap(Player.getName)(S.IsNonEmpty), F.Flow2(Player.getID, errors.OnSome[int]("player %d must have a name"))) + + // readfile :: String -> String -> Task Error String + readfile = F.Curry2(func(encoding, file string) IOE.IOEither[error, string] { + return IOE.Of[error](fmt.Sprintf("content of %s (%s)", file, encoding)) + }) + + // readdir :: String -> Task Error [String] + readdir = IOE.Of[error](A.From("file1", "file2", "file3")) +) + +func Example_solution12A() { + // getJsons :: Map Route Route -> Task Error (Map Route JSON) + getJsons := IOE.TraverseRecord[string](httpGet) + + fmt.Println(getJsons(routes)()) + + // Output: + // Right[map[string]string](map[/:json for / /about:json for /about]) +} + +func Example_solution12B() { + // startGame :: [Player] -> [Either Error String] + startGame := F.Flow2( + E.TraverseArray(validatePlayer), + E.MapTo[error, []Player]("Game started"), + ) + + fmt.Println(startGame(A.From(playerAlbert, playerTheresa))) + fmt.Println(startGame(A.From(playerAlbert, Player{Id: 4}))) + + // Output: + // Right[string](Game started) + // Left[*errors.errorString](player 4 must have a name) +} + +func Example_solution12C() { + traverseO := O.Traverse[string]( + IOE.Of[error, O.Option[string]], + IOE.Map[error, string, O.Option[string]], + ) + + // readFirst :: String -> Task Error (Maybe String) + readFirst := F.Pipe2( + readdir, + IOE.Map[error](A.Head[string]), + IOE.Chain(traverseO(readfile("utf-8"))), + ) + + fmt.Println(readFirst()) + + // Output: + // Right[option.Option[string]](Some[string](content of file1 (utf-8))) +} diff --git a/v2/samples/mostly-adequate/doc_test.go b/v2/samples/mostly-adequate/doc_test.go new file mode 100644 index 0000000..b6369df --- /dev/null +++ b/v2/samples/mostly-adequate/doc_test.go @@ -0,0 +1,19 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package mostlyadequate is meant to serve as a go "companion" resource to Professor [Frisby's Mostly Adequate Guide]. +// +// [Frisby's Mostly Adequate Guide]: https://github.com/MostlyAdequate/mostly-adequate-guide +package mostlyadequate diff --git a/v2/samples/mostly-adequate/support_test.go b/v2/samples/mostly-adequate/support_test.go new file mode 100644 index 0000000..9f10c84 --- /dev/null +++ b/v2/samples/mostly-adequate/support_test.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mostlyadequate + +import ( + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" +) + +type ( + Car struct { + Name string + Horsepower int + DollarValue float32 + InStock bool + } +) + +func (car Car) getInStock() bool { + return car.InStock +} + +func (car Car) getDollarValue() float32 { + return car.DollarValue +} + +func (car Car) getHorsepower() int { + return car.Horsepower +} + +func (car Car) getName() string { + return car.Name +} + +func average(val []float32) float32 { + return F.Pipe2( + val, + A.Fold(N.MonoidSum[float32]()), + N.Div(float32(len(val))), + ) +} + +var ( + Cars = A.From(Car{ + Name: "Ferrari FF", + Horsepower: 660, + DollarValue: 700000, + InStock: true, + }, Car{ + Name: "Spyker C12 Zagato", + Horsepower: 650, + DollarValue: 648000, + InStock: false, + }, Car{ + Name: "Jaguar XKR-S", + Horsepower: 550, + DollarValue: 132000, + InStock: true, + }, Car{ + Name: "Audi R8", + Horsepower: 525, + DollarValue: 114200, + InStock: false, + }, Car{ + Name: "Aston Martin One-77", + Horsepower: 750, + DollarValue: 1850000, + InStock: true, + }, Car{ + Name: "Pagani Huayra", + Horsepower: 700, + DollarValue: 1300000, + InStock: false, + }) +) diff --git a/v2/samples/presentation/.gitignore b/v2/samples/presentation/.gitignore new file mode 100644 index 0000000..10f7209 --- /dev/null +++ b/v2/samples/presentation/.gitignore @@ -0,0 +1 @@ +~$* \ No newline at end of file diff --git a/v2/samples/presentation/benchmarks/http_test.go b/v2/samples/presentation/benchmarks/http_test.go new file mode 100644 index 0000000..6bda388 --- /dev/null +++ b/v2/samples/presentation/benchmarks/http_test.go @@ -0,0 +1,123 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package benchmarks + +import ( + "context" + "encoding/json" + "io" + "testing" + + HTTP "net/http" + + A "github.com/IBM/fp-go/v2/array" + R "github.com/IBM/fp-go/v2/context/readerioeither" + H "github.com/IBM/fp-go/v2/context/readerioeither/http" + F "github.com/IBM/fp-go/v2/function" + T "github.com/IBM/fp-go/v2/tuple" +) + +type PostItem struct { + UserID uint `json:"userId"` + Id uint `json:"id"` + Title string `json:"title"` + Body string `json:"body"` +} + +type CatFact struct { + Fact string `json:"fact"` +} + +func heterogeneousHTTPRequests(count int) R.ReaderIOEither[[]T.Tuple2[PostItem, CatFact]] { + // prepare the http client + client := H.MakeClient(HTTP.DefaultClient) + // readSinglePost sends a GET request and parses the response as [PostItem] + readSinglePost := H.ReadJSON[PostItem](client) + // readSingleCatFact sends a GET request and parses the response as [CatFact] + readSingleCatFact := H.ReadJSON[CatFact](client) + + single := F.Pipe2( + T.MakeTuple2("https://jsonplaceholder.typicode.com/posts/1", "https://catfact.ninja/fact"), + T.Map2(H.MakeGetRequest, H.MakeGetRequest), + R.TraverseTuple2( + readSinglePost, + readSingleCatFact, + ), + ) + + return F.Pipe1( + A.Replicate(count, single), + R.SequenceArray[T.Tuple2[PostItem, CatFact]], + ) +} + +func heterogeneousHTTPRequestsIdiomatic(count int) ([]T.Tuple2[PostItem, CatFact], error) { + // prepare the http client + var result []T.Tuple2[PostItem, CatFact] + + for i := 0; i < count; i++ { + resp, err := HTTP.Get("https://jsonplaceholder.typicode.com/posts/1") + if err != nil { + return nil, err + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var item PostItem + err = json.Unmarshal(body, &item) + if err != nil { + return nil, err + } + resp, err = HTTP.Get("https://catfact.ninja/fact") + if err != nil { + return nil, err + } + body, err = io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var fact CatFact + err = json.Unmarshal(body, &item) + if err != nil { + return nil, err + } + result = append(result, T.MakeTuple2(item, fact)) + } + return result, nil +} + +// BenchmarkHeterogeneousHttpRequests shows how to execute multiple HTTP requests in parallel when +// the response structure of these requests is different. We use [R.TraverseTuple2] to account for the different types +func BenchmarkHeterogeneousHttpRequests(b *testing.B) { + + count := 100 + var benchResults any + + b.Run("functional", func(b *testing.B) { + for n := 0; n < b.N; n++ { + benchResults = heterogeneousHTTPRequests(count)(context.Background())() + } + }) + + b.Run("idiomatic", func(b *testing.B) { + for n := 0; n < b.N; n++ { + benchResults, _ = heterogeneousHTTPRequestsIdiomatic(count) + } + }) + + globalResult = benchResults +} diff --git a/v2/samples/presentation/benchmarks/map_test.go b/v2/samples/presentation/benchmarks/map_test.go new file mode 100644 index 0000000..6ec81a4 --- /dev/null +++ b/v2/samples/presentation/benchmarks/map_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package benchmarks + +import ( + "math/big" + "strings" + "testing" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" + O "github.com/IBM/fp-go/v2/option" +) + +var ( + createStringSet = createRandom(createRandomString(256))(256) + createIntDataSet = createRandom(randInt(10000))(256) + + globalResult any +) + +func BenchmarkMap(b *testing.B) { + + data := createStringSet() + + var benchResult []string + + b.Run("functional", func(b *testing.B) { + for n := 0; n < b.N; n++ { + benchResult = F.Pipe1( + data, + A.Map(strings.ToUpper), + ) + } + }) + + b.Run("idiomatic", func(b *testing.B) { + for n := 0; n < b.N; n++ { + var result = make([]string, 0, len(data)) + for _, value := range data { + result = append(result, strings.ToUpper(value)) + } + benchResult = result + } + }) + + globalResult = benchResult +} + +func isEven(data int) bool { + return data%2 == 0 +} + +func isPrime(data int) bool { + return big.NewInt(int64(data)).ProbablyPrime(0) +} + +func BenchmarkMapThenFilter(b *testing.B) { + + data := createIntDataSet() + var benchResult []int + + b.Run("functional isPrime", func(b *testing.B) { + for n := 0; n < b.N; n++ { + benchResult = F.Pipe2( + data, + A.Filter(isPrime), + A.Map(N.Div[int](2)), + ) + } + }) + + b.Run("idiomatic isPrime", func(b *testing.B) { + for n := 0; n < b.N; n++ { + var result []int + for _, value := range data { + if isPrime(value) { + result = append(result, value/2) + } + } + benchResult = result + } + }) + b.Run("functional isEven", func(b *testing.B) { + for n := 0; n < b.N; n++ { + benchResult = F.Pipe2( + data, + A.Filter(isEven), + A.Map(N.Div[int](2)), + ) + } + }) + + b.Run("idiomatic isEven", func(b *testing.B) { + for n := 0; n < b.N; n++ { + var result []int + for _, value := range data { + if isEven(value) { + result = append(result, value/2) + } + } + benchResult = result + } + }) + + globalResult = benchResult +} + +func BenchmarkFilterMap(b *testing.B) { + + data := createIntDataSet() + var benchResult []int + + b.Run("functional isPrime", func(b *testing.B) { + for n := 0; n < b.N; n++ { + benchResult = F.Pipe1( + data, + A.FilterMap(F.Flow2( + O.FromPredicate(isPrime), + O.Map(N.Div[int](2)), + )), + ) + } + }) + + b.Run("idiomatic isPrime", func(b *testing.B) { + for n := 0; n < b.N; n++ { + var result []int + for _, value := range data { + if isPrime(value) { + result = append(result, value/2) + } + } + benchResult = result + } + }) + + b.Run("functional isEven", func(b *testing.B) { + for n := 0; n < b.N; n++ { + benchResult = F.Pipe1( + data, + A.FilterMap(F.Flow2( + O.FromPredicate(isEven), + O.Map(N.Div[int](2)), + )), + ) + } + }) + + b.Run("idiomatic isEven", func(b *testing.B) { + for n := 0; n < b.N; n++ { + var result []int + for _, value := range data { + if isEven(value) { + result = append(result, value/2) + } + } + benchResult = result + } + }) + + globalResult = benchResult +} diff --git a/v2/samples/presentation/benchmarks/utils_test.go b/v2/samples/presentation/benchmarks/utils_test.go new file mode 100644 index 0000000..3f136e8 --- /dev/null +++ b/v2/samples/presentation/benchmarks/utils_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package benchmarks + +import ( + "math/rand" + "time" + + A "github.com/IBM/fp-go/v2/array" + B "github.com/IBM/fp-go/v2/bytes" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" +) + +const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +var ( + seededRand = rand.New(rand.NewSource(time.Now().UnixNano())) + randChar = F.Pipe2( + len(charset), + randInt, + IO.Map(charAt), + ) + createRandomString = F.Flow3( + F.Bind2of2(A.Replicate[IO.IO[byte]])(randChar), + IO.SequenceArray[byte], + IO.Map(B.ToString), + ) +) + +func createRandom[T any](single IO.IO[T]) func(size int) IO.IO[[]T] { + return F.Flow2( + F.Bind2of2(A.Replicate[IO.IO[T]])(single), + IO.SequenceArray[T], + ) +} + +func charAt(idx int) byte { + return charset[idx] +} + +func randInt(count int) IO.IO[int] { + return func() int { + return seededRand.Intn(count) + } +} diff --git a/v2/samples/presentation/cover.jpg b/v2/samples/presentation/cover.jpg new file mode 100644 index 0000000..0831740 Binary files /dev/null and b/v2/samples/presentation/cover.jpg differ diff --git a/v2/samples/presentation/examples/data/file1.txt b/v2/samples/presentation/examples/data/file1.txt new file mode 100644 index 0000000..9fb0773 --- /dev/null +++ b/v2/samples/presentation/examples/data/file1.txt @@ -0,0 +1 @@ +Some data \ No newline at end of file diff --git a/v2/samples/presentation/examples/data/file2.json b/v2/samples/presentation/examples/data/file2.json new file mode 100644 index 0000000..d37b1b0 --- /dev/null +++ b/v2/samples/presentation/examples/data/file2.json @@ -0,0 +1,3 @@ +{ + "a": 10 +} \ No newline at end of file diff --git a/v2/samples/presentation/examples/example_composition_test.go b/v2/samples/presentation/examples/example_composition_test.go new file mode 100644 index 0000000..ed31782 --- /dev/null +++ b/v2/samples/presentation/examples/example_composition_test.go @@ -0,0 +1,70 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package examples + +import ( + "fmt" + + A "github.com/IBM/fp-go/v2/array" + F "github.com/IBM/fp-go/v2/function" +) + +func Example_composition_pipe() { + + filter := func(i int) bool { + return i%2 == 0 + } + + double := func(i int) int { + return i * 2 + } + + input := []int{1, 2, 3, 4} + + res := F.Pipe2( + input, + A.Filter(filter), + A.Map(double), + ) + + fmt.Println(res) + + // Output: + // [4 8] +} + +func Example_composition_flow() { + + filter := func(i int) bool { + return i%2 == 0 + } + + double := func(i int) int { + return i * 2 + } + + input := []int{1, 2, 3, 4} + + filterAndDouble := F.Flow2( + A.Filter(filter), + A.Map(double), + ) // func([]int) []int + + fmt.Println(filterAndDouble(input)) + + // Output: + // [4 8] +} diff --git a/v2/samples/presentation/examples/example_either_test.go b/v2/samples/presentation/examples/example_either_test.go new file mode 100644 index 0000000..d4592e9 --- /dev/null +++ b/v2/samples/presentation/examples/example_either_test.go @@ -0,0 +1,103 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package examples + +import ( + "fmt" + "strconv" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + S "github.com/IBM/fp-go/v2/string" +) + +func validatePort(port int) (int, error) { + if port > 0 { + return port, nil + } + return 0, fmt.Errorf("Value %d is not a valid port number", port) +} + +func Example_either_monad() { + + // func(string) E.Either[error, int] + atoi := E.Eitherize1(strconv.Atoi) + // func(int) E.Either[error, int] + valPort := E.Eitherize1(validatePort) + + // func(string) E.Either[error, string] + makeUrl := F.Flow3( + atoi, + E.Chain(valPort), + E.Map[error](S.Format[int]("http://localhost:%d")), + ) + + fmt.Println(makeUrl("8080")) + + // Output: + // Right[string](http://localhost:8080) +} + +func Example_either_idiomatic() { + + makeUrl := func(port string) (string, error) { + parsed, err := strconv.Atoi(port) + if err != nil { + return "", err + } + valid, err := validatePort(parsed) + if err != nil { + return "", err + } + return fmt.Sprintf("http://localhost:%d", valid), nil + } + + url, err := makeUrl("8080") + if err != nil { + panic(err) + } + fmt.Println(url) + + // Output: + // http://localhost:8080 +} + +func Example_either_worlds() { + + // func(string) E.Either[error, int] + atoi := E.Eitherize1(strconv.Atoi) + // func(int) E.Either[error, int] + valPort := E.Eitherize1(validatePort) + + // func(string) E.Either[error, string] + makeUrl := F.Flow3( + atoi, + E.Chain(valPort), + E.Map[error](S.Format[int]("http://localhost:%d")), + ) + + // func(string) (string, error) + makeUrlGo := E.Uneitherize1(makeUrl) + + url, err := makeUrlGo("8080") + if err != nil { + panic(err) + } + fmt.Println(url) + + // Output: + // http://localhost:8080 +} diff --git a/v2/samples/presentation/examples/example_generics_test.go b/v2/samples/presentation/examples/example_generics_test.go new file mode 100644 index 0000000..65aec10 --- /dev/null +++ b/v2/samples/presentation/examples/example_generics_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package examples + +import ( + "fmt" + + N "github.com/IBM/fp-go/v2/number" +) + +// addInts adds two integers +func addInts(left, right int) int { + return left + right +} + +// addNumbers adds two numbers +func addNumbers[T N.Number](left, right T) T { + return left + right +} + +func Example_generics() { + // invoke the non generic version + fmt.Println(addInts(1, 2)) + + // invoke the generic version + fmt.Println(addNumbers(1, 2)) + fmt.Println(addNumbers(1.0, 2.0)) + + // Output: + // 3 + // 3 + // 3 +} diff --git a/v2/samples/presentation/examples/example_hof_test.go b/v2/samples/presentation/examples/example_hof_test.go new file mode 100644 index 0000000..52cce60 --- /dev/null +++ b/v2/samples/presentation/examples/example_hof_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package examples + +import "fmt" + +func captureValue[T any](captured T) func() string { + return func() string { + return fmt.Sprintf("Value: %v", captured) + } +} + +func Example_closure() { + + hof := captureValue("Carsten") // func() string + + fmt.Println(hof()) + + // Output: + // Value: Carsten +} diff --git a/v2/samples/presentation/examples/example_immutability_test.go b/v2/samples/presentation/examples/example_immutability_test.go new file mode 100644 index 0000000..ef8308f --- /dev/null +++ b/v2/samples/presentation/examples/example_immutability_test.go @@ -0,0 +1,156 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package examples + +import ( + "fmt" + "strings" + + F "github.com/IBM/fp-go/v2/function" + N "github.com/IBM/fp-go/v2/number" + L "github.com/IBM/fp-go/v2/optics/lens" +) + +type Person struct { + name string + age int +} + +func (p Person) GetName() string { + return p.name +} + +func (p Person) GetAge() int { + return p.age +} + +func (p Person) SetName(name string) Person { + p.name = name + return p +} + +func (p Person) SetAge(age int) Person { + p.age = age + return p +} + +type Address struct { + city string +} + +func (a Address) GetCity() string { + return a.city +} + +func (a Address) SetCity(city string) Address { + a.city = city + return a +} + +type Client struct { + person Person + address Address +} + +func (c Client) GetPerson() Person { + return c.person +} + +func (c Client) SetPerson(person Person) Client { + c.person = person + return c +} + +func (c Client) GetAddress() Address { + return c.address +} + +func (c Client) SetAddress(address Address) Client { + c.address = address + return c +} + +func MakePerson(name string, age int) Person { + return Person{name, age} +} + +func MakeClient(city string, name string, age int) Client { + return Client{person: Person{name, age}, address: Address{city}} +} + +func Example_immutability_struct() { + p1 := MakePerson("Carsten", 53) + + // func(int) func(Person) Person + setAge := F.Curry2(F.Swap(Person.SetAge)) + + p2 := F.Pipe1( + p1, + setAge(54), + ) + + fmt.Println(p1) + fmt.Println(p2) + + // Output: + // {Carsten 53} + // {Carsten 54} +} + +func Example_immutability_optics() { + + // Lens[Person, int] + ageLens := L.MakeLens(Person.GetAge, Person.SetAge) + // func(Person) Person + incAge := L.Modify[Person](N.Inc[int])(ageLens) + + p1 := MakePerson("Carsten", 53) + p2 := incAge(p1) + + fmt.Println(p1) + fmt.Println(p2) + + // Output: + // {Carsten 53} + // {Carsten 54} +} + +func Example_immutability_lenses() { + + // Lens[Person, string] + nameLens := L.MakeLens(Person.GetName, Person.SetName) + // Lens[Client, Person] + personLens := L.MakeLens(Client.GetPerson, Client.SetPerson) + + // Lens[Client, string] + clientNameLens := F.Pipe1( + personLens, + L.Compose[Client](nameLens), + ) + // func(Client) Client + upperName := L.Modify[Client](strings.ToUpper)(clientNameLens) + + c1 := MakeClient("Böblingen", "Carsten", 53) + + c2 := upperName(c1) + + fmt.Println(c1) + fmt.Println(c2) + + // Output: + // {{Carsten 53} {Böblingen}} + // {{CARSTEN 53} {Böblingen}} +} diff --git a/v2/samples/presentation/examples/example_map_test.go b/v2/samples/presentation/examples/example_map_test.go new file mode 100644 index 0000000..0bf1176 --- /dev/null +++ b/v2/samples/presentation/examples/example_map_test.go @@ -0,0 +1,64 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package examples + +import ( + "fmt" + + A "github.com/IBM/fp-go/v2/array" + N "github.com/IBM/fp-go/v2/number" +) + +func Example_map() { + + f := func(i int) int { + return i * 2 + } + + input := []int{1, 2, 3, 4} + + // idiomatic go + res1 := make([]int, 0, len(input)) + for _, i := range input { + res1 = append(res1, f(i)) + } + fmt.Println(res1) + + // map + res2 := A.Map(f)(input) + fmt.Println(res2) + + // Output: + // [2 4 6 8] + // [2 4 6 8] +} + +func Example_reduce() { + + input := []int{1, 2, 3, 4} + + // reduce + red := A.Reduce(N.MonoidSum[int]().Concat, 0)(input) + fmt.Println(red) + + // fold + fld := A.Fold(N.MonoidSum[int]())(input) + fmt.Println(fld) + + // Output: + // 10 + // 10 +} diff --git a/v2/samples/presentation/examples/example_sideeffect_test.go b/v2/samples/presentation/examples/example_sideeffect_test.go new file mode 100644 index 0000000..a19e2cd --- /dev/null +++ b/v2/samples/presentation/examples/example_sideeffect_test.go @@ -0,0 +1,105 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package examples + +import ( + "encoding/json" + "fmt" + "os" + + B "github.com/IBM/fp-go/v2/bytes" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + "github.com/IBM/fp-go/v2/ioeither/file" + J "github.com/IBM/fp-go/v2/json" + T "github.com/IBM/fp-go/v2/tuple" +) + +type Sample struct { + Value int `json:"a"` +} + +func (s Sample) getValue() int { + return s.Value +} + +func Example_io_flow() { + + // IOE.IOEither[error, string] + text := F.Pipe2( + "data/file1.txt", + file.ReadFile, + IOE.Map[error](B.ToString), + ) + + // IOE.IOEither[error, int] + value := F.Pipe3( + "data/file2.json", + file.ReadFile, + IOE.ChainEitherK(J.Unmarshal[Sample]), + IOE.Map[error](Sample.getValue), + ) + + // IOE.IOEither[error, string] + result := F.Pipe1( + IOE.SequenceT2(text, value), + IOE.Map[error](func(res T.Tuple2[string, int]) string { + return fmt.Sprintf("Text: %s, Number: %d", res.F1, res.F2) + }), + ) + + fmt.Println(result()) + + // Output: + // Right[string](Text: Some data, Number: 10) + +} + +func io_flow_idiomatic() error { + + // []byte + file1AsBytes, err := os.ReadFile("data/file1.txt") + if err != nil { + return err + } + // string + text := string(file1AsBytes) + + // []byte + file2AsBytes, err := os.ReadFile("data/file2.json") + if err != nil { + return err + } + var value Sample + if err := json.Unmarshal(file2AsBytes, &value); err != nil { + return err + } + // string + result := fmt.Sprintf("Text: %s, Number: %d", text, value.Value) + + fmt.Println(result) + + return nil +} + +func Example_io_flow_idiomatic() { + if err := io_flow_idiomatic(); err != nil { + panic(err) + } + + // Output: + // Text: Some data, Number: 10 +} diff --git a/v2/samples/presentation/examples/examples_monad_test.go b/v2/samples/presentation/examples/examples_monad_test.go new file mode 100644 index 0000000..c12f673 --- /dev/null +++ b/v2/samples/presentation/examples/examples_monad_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package examples + +type HKT[T any] struct { +} + +// Pointed +func Of[A any](A) HKT[A] { return HKT[A]{} } + +// Functor +func Map[A, B any](func(A) B) func(HKT[A]) HKT[B] { return func(HKT[A]) HKT[B] { return HKT[B]{} } } +func MapTo[A, B any](A) func(HKT[A]) HKT[B] { return func(HKT[A]) HKT[B] { return HKT[B]{} } } + +// Chain +func Chain[A, B any](func(A) HKT[B]) func(HKT[A]) HKT[B] { + return func(HKT[A]) HKT[B] { return HKT[B]{} } +} +func ChainTo[A, B any](HKT[B]) func(HKT[A]) HKT[B] { + return func(HKT[A]) HKT[B] { return HKT[B]{} } +} +func ChainFirst[A, B any](func(A) HKT[B]) func(HKT[A]) HKT[A] { + return func(HKT[A]) HKT[A] { return HKT[A]{} } +} + +// Apply +func Ap[A, B any](HKT[A]) func(HKT[func(A) B]) HKT[B] { + return func(HKT[func(A) B]) HKT[B] { return HKT[B]{} } +} + +// Derived +func Flatten[A, B any](HKT[HKT[A]]) HKT[A] { + return HKT[A]{} +} +func Reduce[A, B any](func(B, A) B, B) func(HKT[A]) HKT[B] { + return func(HKT[A]) HKT[B] { return HKT[B]{} } +} diff --git a/v2/samples/presentation/introduction.pptx b/v2/samples/presentation/introduction.pptx new file mode 100644 index 0000000..6c1042d Binary files /dev/null and b/v2/samples/presentation/introduction.pptx differ diff --git a/v2/samples/readerioeither/example1/reader_test.go b/v2/samples/readerioeither/example1/reader_test.go new file mode 100644 index 0000000..d1d4ac5 --- /dev/null +++ b/v2/samples/readerioeither/example1/reader_test.go @@ -0,0 +1,89 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package example1 + +import ( + "os" + + B "github.com/IBM/fp-go/v2/bytes" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + I "github.com/IBM/fp-go/v2/identity" + IOE "github.com/IBM/fp-go/v2/ioeither" + J "github.com/IBM/fp-go/v2/json" + RIOE "github.com/IBM/fp-go/v2/readerioeither" +) + +type ( + WriterType = func([]byte) IOE.IOEither[error, []byte] + + Dependencies struct { + Writer WriterType + } +) + +func getWriter(deps *Dependencies) WriterType { + return deps.Writer +} + +// SerializeToWriter marshals the input to JSON and persists the result via the [Writer] passed in via the [*Dependencies] +func SerializeToWriter[A any](data A) RIOE.ReaderIOEither[*Dependencies, error, []byte] { + return F.Pipe1( + RIOE.Ask[*Dependencies, error](), + RIOE.ChainIOEitherK[*Dependencies](F.Flow2( + getWriter, + F.Pipe2( + data, + J.Marshal[A], + E.Fold(F.Flow2( + IOE.Left[[]byte, error], + F.Constant1[WriterType, IOE.IOEither[error, []byte]], + ), I.Ap[IOE.IOEither[error, []byte], []byte]), + ), + )), + ) +} + +func ExampleReaderIOEither() { + + // writeToStdOut implements a writer to stdout + writeToStdOut := func(data []byte) IOE.IOEither[error, []byte] { + return IOE.TryCatchError(func() ([]byte, error) { + _, err := os.Stdout.Write(data) + return data, err + }) + } + + deps := Dependencies{ + Writer: writeToStdOut, + } + + data := map[string]string{ + "a": "b", + "c": "d", + } + + // writeData will persist to a configurable target + writeData := F.Pipe1( + SerializeToWriter(data), + RIOE.Map[*Dependencies, error](B.ToString), + ) + + _ = writeData(&deps)() + + // Output: + // {"a":"b","c":"d"} +} diff --git a/v2/samples/readfile/data/file.json b/v2/samples/readfile/data/file.json new file mode 100644 index 0000000..6f6b333 --- /dev/null +++ b/v2/samples/readfile/data/file.json @@ -0,0 +1,3 @@ +{ + "data": "Carsten" +} \ No newline at end of file diff --git a/v2/samples/readfile/data/file1.json b/v2/samples/readfile/data/file1.json new file mode 100644 index 0000000..5d25584 --- /dev/null +++ b/v2/samples/readfile/data/file1.json @@ -0,0 +1,3 @@ +{ + "data": "file1" +} \ No newline at end of file diff --git a/v2/samples/readfile/data/file2.json b/v2/samples/readfile/data/file2.json new file mode 100644 index 0000000..03f5d1a --- /dev/null +++ b/v2/samples/readfile/data/file2.json @@ -0,0 +1,3 @@ +{ + "data": "file2" +} \ No newline at end of file diff --git a/v2/samples/readfile/data/file3.json b/v2/samples/readfile/data/file3.json new file mode 100644 index 0000000..27ed7e8 --- /dev/null +++ b/v2/samples/readfile/data/file3.json @@ -0,0 +1,3 @@ +{ + "data": "file3" +} \ No newline at end of file diff --git a/v2/samples/readfile/readfile_test.go b/v2/samples/readfile/readfile_test.go new file mode 100644 index 0000000..e173e1c --- /dev/null +++ b/v2/samples/readfile/readfile_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package readfile + +import ( + "context" + "fmt" + "testing" + + A "github.com/IBM/fp-go/v2/array" + R "github.com/IBM/fp-go/v2/context/readerioeither" + "github.com/IBM/fp-go/v2/context/readerioeither/file" + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IO "github.com/IBM/fp-go/v2/io" + J "github.com/IBM/fp-go/v2/json" + "github.com/stretchr/testify/assert" +) + +type RecordType struct { + Data string `json:"data"` +} + +// TestReadSingleFile reads the content of a file from disk and parses it into +// a struct +func TestReadSingleFile(t *testing.T) { + + data := F.Pipe2( + file.ReadFile("./data/file.json"), + R.ChainEitherK(J.Unmarshal[RecordType]), + R.ChainFirstIOK(IO.Logf[RecordType]("Log: %v")), + ) + + result := data(context.Background()) + + assert.Equal(t, E.Of[error](RecordType{"Carsten"}), result()) +} + +func idxToFilename(idx int) string { + return fmt.Sprintf("./data/file%d.json", idx+1) +} + +// TestReadMultipleFiles reads the content of a multiple from disk and parses them into +// structs +func TestReadMultipleFiles(t *testing.T) { + + data := F.Pipe2( + A.MakeBy(3, idxToFilename), + R.TraverseArray(F.Flow3( + file.ReadFile, + R.ChainEitherK(J.Unmarshal[RecordType]), + R.ChainFirstIOK(IO.Logf[RecordType]("Log Single: %v")), + )), + R.ChainFirstIOK(IO.Logf[[]RecordType]("Log Result: %v")), + ) + + result := data(context.Background()) + + assert.Equal(t, E.Of[error](A.From(RecordType{"file1"}, RecordType{"file2"}, RecordType{"file3"})), result()) +} diff --git a/v2/samples/tuples/option_test.go b/v2/samples/tuples/option_test.go new file mode 100644 index 0000000..71bf8dd --- /dev/null +++ b/v2/samples/tuples/option_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tuples + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOEF "github.com/IBM/fp-go/v2/ioeither/file" + IOEG "github.com/IBM/fp-go/v2/ioeither/generic" + IOO "github.com/IBM/fp-go/v2/iooption" +) + +func TestIOEitherToOption1(t *testing.T) { + tmpDir := t.TempDir() + content := []byte("abc") + + resIOO := F.Pipe2( + content, + IOEF.WriteFile(filepath.Join(tmpDir, "test.txt"), os.ModePerm), + IOEG.Fold[IOE.IOEither[error, []byte]]( + IOO.Of[error], + F.Ignore1of1[[]byte](IOO.None[error]), + ), + ) + + fmt.Println(resIOO()) +} + +func TestIOEitherToOption2(t *testing.T) { + tmpDir := t.TempDir() + content := []byte("abc") + + resIOO := F.Pipe3( + content, + IOEF.WriteFile(filepath.Join(tmpDir, "test.txt"), os.ModePerm), + IOE.Swap[error, []byte], + IOE.ToIOOption[[]byte, error], + ) + + fmt.Println(resIOO()) +} diff --git a/v2/samples/tuples/samples/data.txt b/v2/samples/tuples/samples/data.txt new file mode 100644 index 0000000..f2ba8f8 --- /dev/null +++ b/v2/samples/tuples/samples/data.txt @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/v2/samples/tuples/tuple_test.go b/v2/samples/tuples/tuple_test.go new file mode 100644 index 0000000..0d89eaa --- /dev/null +++ b/v2/samples/tuples/tuple_test.go @@ -0,0 +1,128 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tuples + +import ( + "bytes" + "io" + "strings" + "testing" + + E "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + IOE "github.com/IBM/fp-go/v2/ioeither" + IOEF "github.com/IBM/fp-go/v2/ioeither/file" + T "github.com/IBM/fp-go/v2/tuple" + "github.com/stretchr/testify/assert" +) + +func sampleConvertDocx(r io.Reader) (string, map[string]string, error) { + content, err := io.ReadAll(r) + return string(content), map[string]string{}, err +} + +func TestSampleConvertDocx1(t *testing.T) { + // this conversion approach has the disadvantage that it exhausts the reader + // so we cannot invoke the resulting IOEither multiple times + convertDocx := func(r io.Reader) IOE.IOEither[error, T.Tuple2[string, map[string]string]] { + return IOE.TryCatchError(func() (T.Tuple2[string, map[string]string], error) { + text, meta, err := sampleConvertDocx(r) + return T.MakeTuple2(text, meta), err + }) + } + + rdr := strings.NewReader("abc") + resIOE := convertDocx(rdr) + + resE := resIOE() + + assert.True(t, E.IsRight(resE)) +} + +func TestSampleConvertDocx2(t *testing.T) { + // this approach assumes that `sampleConvertDocx` does not have any side effects + // other than reading from a `Reader`. As a consequence it can be a pure function itself. + // The disadvantage is that its input has to exist in memory which is probably not a good + // idea for large inputs + convertDocx := func(data []byte) E.Either[error, T.Tuple2[string, map[string]string]] { + text, meta, err := sampleConvertDocx(bytes.NewReader(data)) + return E.TryCatchError(T.MakeTuple2(text, meta), err) + } + + resE := convertDocx([]byte("abc")) + + assert.True(t, E.IsRight(resE)) +} + +// onClose closes a closeable resource +func onClose[R io.Closer](r R) IOE.IOEither[error, R] { + return IOE.TryCatchError(func() (R, error) { + return r, r.Close() + }) +} + +// convertDocx3 takes an `acquire` function that creates an instance or a [ReaderCloser] whenever the resulting [IOEither] is invoked. Since +// we return a [Closer] the instance will be closed after use, automatically. This design makes sure that the resulting [IOEither] can be invoked +// as many times as necessary +func convertDocx3[R io.ReadCloser](acquire IOE.IOEither[error, R]) IOE.IOEither[error, T.Tuple2[string, map[string]string]] { + return IOE.WithResource[T.Tuple2[string, map[string]string]]( + acquire, + onClose[R])( + func(r R) IOE.IOEither[error, T.Tuple2[string, map[string]string]] { + return IOE.TryCatchError(func() (T.Tuple2[string, map[string]string], error) { + text, meta, err := sampleConvertDocx(r) + return T.MakeTuple2(text, meta), err + }) + }, + ) +} + +// convertDocx4 takes an `acquire` function that creates an instance or a [Reader] whenever the resulting [IOEither] is invoked. +// This design makes sure that the resulting [IOEither] can be invoked +// as many times as necessary +func convertDocx4[R io.Reader](acquire IOE.IOEither[error, R]) IOE.IOEither[error, T.Tuple2[string, map[string]string]] { + return F.Pipe1( + acquire, + IOE.Chain(func(r R) IOE.IOEither[error, T.Tuple2[string, map[string]string]] { + return IOE.TryCatchError(func() (T.Tuple2[string, map[string]string], error) { + text, meta, err := sampleConvertDocx(r) + return T.MakeTuple2(text, meta), err + }) + }), + ) +} + +func TestSampleConvertDocx3(t *testing.T) { + // IOEither that creates the reader + acquire := IOEF.Open("./samples/data.txt") + + resIOE := convertDocx3(acquire) + resE := resIOE() + + assert.True(t, E.IsRight(resE)) +} + +func TestSampleConvertDocx4(t *testing.T) { + // IOEither that creates the reader + acquire := IOE.FromIO[error](func() *strings.Reader { + return strings.NewReader("abc") + }) + + resIOE := convertDocx4(acquire) + resE := resIOE() + + assert.True(t, E.IsRight(resE)) +} diff --git a/v2/semigroup/alt.go b/v2/semigroup/alt.go new file mode 100644 index 0000000..b527a2d --- /dev/null +++ b/v2/semigroup/alt.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package semigroup + +func AltSemigroup[HKTA any, LAZYHKTA ~func() HKTA]( + falt func(HKTA, LAZYHKTA) HKTA, + +) Semigroup[HKTA] { + + return MakeSemigroup( + func(first, second HKTA) HKTA { + return falt(first, func() HKTA { return second }) + }, + ) +} diff --git a/v2/semigroup/apply.go b/v2/semigroup/apply.go new file mode 100644 index 0000000..c1d08df --- /dev/null +++ b/v2/semigroup/apply.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package semigroup + +import ( + F "github.com/IBM/fp-go/v2/function" +) + +/* +* +HKTA = HKT
+HKTFA = HKT +*/ +func ApplySemigroup[A, HKTA, HKTFA any]( + fmap func(HKTA, func(A) func(A) A) HKTFA, + fap func(HKTFA, HKTA) HKTA, + + s Semigroup[A], +) Semigroup[HKTA] { + + cb := F.Curry2(s.Concat) + return MakeSemigroup(func(first HKTA, second HKTA) HKTA { + return fap(fmap(first, cb), second) + }) +} diff --git a/v2/semigroup/array.go b/v2/semigroup/array.go new file mode 100644 index 0000000..8fe1602 --- /dev/null +++ b/v2/semigroup/array.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package semigroup + +import ( + M "github.com/IBM/fp-go/v2/magma" +) + +func GenericMonadConcatAll[GA ~[]A, A any](s Semigroup[A]) func(GA, A) A { + return M.GenericMonadConcatAll[GA](M.MakeMagma(s.Concat)) +} + +func GenericConcatAll[GA ~[]A, A any](s Semigroup[A]) func(A) func(GA) A { + return M.GenericConcatAll[GA](M.MakeMagma(s.Concat)) +} + +func MonadConcatAll[A any](s Semigroup[A]) func([]A, A) A { + return GenericMonadConcatAll[[]A](s) +} + +func ConcatAll[A any](s Semigroup[A]) func(A) func([]A) A { + return GenericConcatAll[[]A](s) +} diff --git a/v2/semigroup/ord/semigroup.go b/v2/semigroup/ord/semigroup.go new file mode 100644 index 0000000..f275238 --- /dev/null +++ b/v2/semigroup/ord/semigroup.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ord + +import ( + O "github.com/IBM/fp-go/v2/ord" + S "github.com/IBM/fp-go/v2/semigroup" +) + +// Max gets a semigroup where `concat` will return the maximum, based on the provided order. +func Max[A any](o O.Ord[A]) S.Semigroup[A] { + return S.MakeSemigroup(O.Max(o)) +} + +// Min gets a semigroup where `concat` will return the minimum, based on the provided order. +func Min[A any](o O.Ord[A]) S.Semigroup[A] { + return S.MakeSemigroup(O.Min(o)) +} diff --git a/v2/semigroup/semigroup.go b/v2/semigroup/semigroup.go new file mode 100644 index 0000000..3a327f9 --- /dev/null +++ b/v2/semigroup/semigroup.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package semigroup + +import ( + F "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/magma" +) + +type Semigroup[A any] interface { + M.Magma[A] +} + +type semigroup[A any] struct { + c func(A, A) A +} + +func (self semigroup[A]) Concat(x A, y A) A { + return self.c(x, y) +} + +func MakeSemigroup[A any](c func(A, A) A) Semigroup[A] { + return semigroup[A]{c: c} +} + +// Reverse returns The dual of a `Semigroup`, obtained by swapping the arguments of `concat`. +func Reverse[A any](m Semigroup[A]) Semigroup[A] { + return MakeSemigroup(M.Reverse[A](m).Concat) +} + +// FunctionSemigroup forms a semigroup as long as you can provide a semigroup for the codomain. +func FunctionSemigroup[A, B any](s Semigroup[B]) Semigroup[func(A) B] { + return MakeSemigroup(func(f func(A) B, g func(A) B) func(A) B { + return func(a A) B { + return s.Concat(f(a), g(a)) + } + }) +} + +// First always returns the first argument. +func First[A any]() Semigroup[A] { + return MakeSemigroup(F.First[A, A]) +} + +// Last always returns the last argument. +func Last[A any]() Semigroup[A] { + return MakeSemigroup(F.Second[A, A]) +} + +// ToMagma converts a semigroup to a magma +func ToMagma[A any](s Semigroup[A]) M.Magma[A] { + return s +} diff --git a/v2/semigroup/semigroup_test.go b/v2/semigroup/semigroup_test.go new file mode 100644 index 0000000..b2f57d8 --- /dev/null +++ b/v2/semigroup/semigroup_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package semigroup + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFirst(t *testing.T) { + + first := First[int]() + + assert.Equal(t, 1, first.Concat(1, 2)) +} + +func TestLast(t *testing.T) { + + last := Last[int]() + + assert.Equal(t, 2, last.Concat(1, 2)) +} diff --git a/v2/state/eq.go b/v2/state/eq.go new file mode 100644 index 0000000..cc994cd --- /dev/null +++ b/v2/state/eq.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package state + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + G "github.com/IBM/fp-go/v2/state/generic" +) + +// Constructs an equal predicate for a [State] +func Eq[S, A any](w EQ.Eq[S], a EQ.Eq[A]) func(S) EQ.Eq[State[S, A]] { + return G.Eq[State[S, A]](w, a) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[S, A comparable]() func(S) EQ.Eq[State[S, A]] { + return G.FromStrictEquals[State[S, A]]() +} diff --git a/v2/state/generic/eq.go b/v2/state/generic/eq.go new file mode 100644 index 0000000..f281072 --- /dev/null +++ b/v2/state/generic/eq.go @@ -0,0 +1,36 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + P "github.com/IBM/fp-go/v2/pair" +) + +// Constructs an equal predicate for a [State] +func Eq[GA ~func(S) P.Pair[A, S], S, A any](w EQ.Eq[S], a EQ.Eq[A]) func(S) EQ.Eq[GA] { + eqp := P.Eq(a, w) + return func(s S) EQ.Eq[GA] { + return EQ.FromEquals(func(l, r GA) bool { + return eqp.Equals(l(s), r(s)) + }) + } +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[GA ~func(S) P.Pair[A, S], S, A comparable]() func(S) EQ.Eq[GA] { + return Eq[GA](EQ.FromStrictEquals[S](), EQ.FromStrictEquals[A]()) +} diff --git a/v2/state/generic/monad.go b/v2/state/generic/monad.go new file mode 100644 index 0000000..f5c48da --- /dev/null +++ b/v2/state/generic/monad.go @@ -0,0 +1,88 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + "github.com/IBM/fp-go/v2/internal/applicative" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" + P "github.com/IBM/fp-go/v2/pair" +) + +type statePointed[GA ~func(S) P.Pair[A, S], S, A any] struct{} + +type stateFunctor[GB ~func(S) P.Pair[B, S], GA ~func(S) P.Pair[A, S], S, A, B any] struct{} + +type stateApplicative[GB ~func(S) P.Pair[B, S], GAB ~func(S) P.Pair[func(A) B, S], GA ~func(S) P.Pair[A, S], S, A, B any] struct{} + +type stateMonad[GB ~func(S) P.Pair[B, S], GAB ~func(S) P.Pair[func(A) B, S], GA ~func(S) P.Pair[A, S], S, A, B any] struct{} + +func (o *statePointed[GA, S, A]) Of(a A) GA { + return Of[GA](a) +} + +func (o *stateApplicative[GB, GAB, GA, S, A, B]) Of(a A) GA { + return Of[GA](a) +} + +func (o *stateMonad[GB, GAB, GA, S, A, B]) Of(a A) GA { + return Of[GA](a) +} + +func (o *stateFunctor[GB, GA, S, A, B]) Map(f func(A) B) func(GA) GB { + return Map[GB, GA](f) +} + +func (o *stateApplicative[GB, GAB, GA, S, A, B]) Map(f func(A) B) func(GA) GB { + return Map[GB, GA](f) +} + +func (o *stateMonad[GB, GAB, GA, S, A, B]) Map(f func(A) B) func(GA) GB { + return Map[GB, GA](f) +} + +func (o *stateMonad[GB, GAB, GA, S, A, B]) Chain(f func(A) GB) func(GA) GB { + return Chain[GB, GA](f) +} + +func (o *stateApplicative[GB, GAB, GA, S, A, B]) Ap(fa GA) func(GAB) GB { + return Ap[GB, GAB, GA](fa) +} + +func (o *stateMonad[GB, GAB, GA, S, A, B]) Ap(fa GA) func(GAB) GB { + return Ap[GB, GAB, GA](fa) +} + +// Pointed implements the pointed operations for [Writer] +func Pointed[GA ~func(S) P.Pair[A, S], S, A any]() pointed.Pointed[A, GA] { + return &statePointed[GA, S, A]{} +} + +// Functor implements the functor operations for [Writer] +func Functor[GB ~func(S) P.Pair[B, S], GA ~func(S) P.Pair[A, S], S, A, B any]() functor.Functor[A, B, GA, GB] { + return &stateFunctor[GB, GA, S, A, B]{} +} + +// Applicative implements the applicative operations for [Writer] +func Applicative[GB ~func(S) P.Pair[B, S], GAB ~func(S) P.Pair[func(A) B, S], GA ~func(S) P.Pair[A, S], S, A, B any]() applicative.Applicative[A, B, GA, GB, GAB] { + return &stateApplicative[GB, GAB, GA, S, A, B]{} +} + +// Monad implements the monadic operations for [Writer] +func Monad[GB ~func(S) P.Pair[B, S], GAB ~func(S) P.Pair[func(A) B, S], GA ~func(S) P.Pair[A, S], S, A, B any]() monad.Monad[A, B, GA, GB, GAB] { + return &stateMonad[GB, GAB, GA, S, A, B]{} +} diff --git a/v2/state/generic/state.go b/v2/state/generic/state.go new file mode 100644 index 0000000..75abb0b --- /dev/null +++ b/v2/state/generic/state.go @@ -0,0 +1,131 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/v2/function" + C "github.com/IBM/fp-go/v2/internal/chain" + FC "github.com/IBM/fp-go/v2/internal/functor" + P "github.com/IBM/fp-go/v2/pair" +) + +var ( + undefined any = struct{}{} +) + +func Get[GA ~func(S) P.Pair[S, S], S any]() GA { + return P.Of[S] +} + +func Gets[GA ~func(S) P.Pair[A, S], FCT ~func(S) A, A, S any](f FCT) GA { + return func(s S) P.Pair[A, S] { + return P.MakePair(f(s), s) + } +} + +func Put[GA ~func(S) P.Pair[any, S], S any]() GA { + return F.Bind1st(P.MakePair[any, S], undefined) +} + +func Modify[GA ~func(S) P.Pair[any, S], FCT ~func(S) S, S any](f FCT) GA { + return F.Flow2( + f, + F.Bind1st(P.MakePair[any, S], undefined), + ) +} + +func Of[GA ~func(S) P.Pair[A, S], S, A any](a A) GA { + return F.Bind1st(P.MakePair[A, S], a) +} + +func MonadMap[GB ~func(S) P.Pair[B, S], GA ~func(S) P.Pair[A, S], FCT ~func(A) B, S, A, B any](fa GA, f FCT) GB { + return func(s S) P.Pair[B, S] { + p2 := fa(s) + return P.MakePair(f(P.Head(p2)), P.Tail(p2)) + } +} + +func Map[GB ~func(S) P.Pair[B, S], GA ~func(S) P.Pair[A, S], FCT ~func(A) B, S, A, B any](f FCT) func(GA) GB { + return F.Bind2nd(MonadMap[GB, GA, FCT, S, A, B], f) +} + +func MonadChain[GB ~func(S) P.Pair[B, S], GA ~func(S) P.Pair[A, S], FCT ~func(A) GB, S, A, B any](fa GA, f FCT) GB { + return func(s S) P.Pair[B, S] { + a := fa(s) + return f(P.Head(a))(P.Tail(a)) + } +} + +func Chain[GB ~func(S) P.Pair[B, S], GA ~func(S) P.Pair[A, S], FCT ~func(A) GB, S, A, B any](f FCT) func(GA) GB { + return F.Bind2nd(MonadChain[GB, GA, FCT, S, A, B], f) +} + +func MonadAp[GB ~func(S) P.Pair[B, S], GAB ~func(S) P.Pair[func(A) B, S], GA ~func(S) P.Pair[A, S], S, A, B any](fab GAB, fa GA) GB { + return func(s S) P.Pair[B, S] { + f := fab(s) + a := fa(P.Tail(f)) + + return P.MakePair(P.Head(f)(P.Head(a)), P.Tail(a)) + } +} + +func Ap[GB ~func(S) P.Pair[B, S], GAB ~func(S) P.Pair[func(A) B, S], GA ~func(S) P.Pair[A, S], S, A, B any](ga GA) func(GAB) GB { + return F.Bind2nd(MonadAp[GB, GAB, GA, S, A, B], ga) +} + +func MonadChainFirst[GB ~func(S) P.Pair[B, S], GA ~func(S) P.Pair[A, S], FCT ~func(A) GB, S, A, B any](ma GA, f FCT) GA { + return C.MonadChainFirst( + MonadChain[GA, GA, func(A) GA], + MonadMap[GA, GB, func(B) A], + ma, + f, + ) +} + +func ChainFirst[GB ~func(S) P.Pair[B, S], GA ~func(S) P.Pair[A, S], FCT ~func(A) GB, S, A, B any](f FCT) func(GA) GA { + return C.ChainFirst( + Chain[GA, GA, func(A) GA], + Map[GA, GB, func(B) A], + f, + ) +} + +func Flatten[GAA ~func(S) P.Pair[GA, S], GA ~func(S) P.Pair[A, S], S, A any](mma GAA) GA { + return MonadChain[GA, GAA, func(GA) GA](mma, F.Identity[GA]) +} + +func Execute[GA ~func(S) P.Pair[A, S], S, A any](s S) func(GA) S { + return func(fa GA) S { + return P.Tail(fa(s)) + } +} + +func Evaluate[GA ~func(S) P.Pair[A, S], S, A any](s S) func(GA) A { + return func(fa GA) A { + return P.Head(fa(s)) + } +} + +func MonadFlap[FAB ~func(A) B, GFAB ~func(S) P.Pair[FAB, S], GB ~func(S) P.Pair[B, S], S, A, B any](fab GFAB, a A) GB { + return FC.MonadFlap( + MonadMap[GB, GFAB, func(FAB) B], + fab, + a) +} + +func Flap[FAB ~func(A) B, GFAB ~func(S) P.Pair[FAB, S], GB ~func(S) P.Pair[B, S], S, A, B any](a A) func(GFAB) GB { + return FC.Flap(Map[GB, GFAB, func(FAB) B], a) +} diff --git a/v2/state/monad.go b/v2/state/monad.go new file mode 100644 index 0000000..722ba4a --- /dev/null +++ b/v2/state/monad.go @@ -0,0 +1,44 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package state + +import ( + "github.com/IBM/fp-go/v2/internal/applicative" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" + G "github.com/IBM/fp-go/v2/state/generic" +) + +// Pointed implements the pointed operations for [State] +func Pointed[S, A any]() pointed.Pointed[A, State[S, A]] { + return G.Pointed[State[S, A], S, A]() +} + +// Functor implements the pointed operations for [State] +func Functor[S, A, B any]() functor.Functor[A, B, State[S, A], State[S, B]] { + return G.Functor[State[S, B], State[S, A], S, A, B]() +} + +// Applicative implements the applicative operations for [State] +func Applicative[S, A, B any]() applicative.Applicative[A, B, State[S, A], State[S, B], State[S, func(A) B]] { + return G.Applicative[State[S, B], State[S, func(A) B], State[S, A]]() +} + +// Monad implements the monadic operations for [State] +func Monad[S, A, B any]() monad.Monad[A, B, State[S, A], State[S, B], State[S, func(A) B]] { + return G.Monad[State[S, B], State[S, func(A) B], State[S, A]]() +} diff --git a/v2/state/state.go b/v2/state/state.go new file mode 100644 index 0000000..75c64c8 --- /dev/null +++ b/v2/state/state.go @@ -0,0 +1,97 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package state + +import ( + P "github.com/IBM/fp-go/v2/pair" + R "github.com/IBM/fp-go/v2/reader" + G "github.com/IBM/fp-go/v2/state/generic" +) + +// State represents an operation on top of a current [State] that produces a value and a new [State] +type State[S, A any] R.Reader[S, P.Pair[A, S]] + +func Get[S any]() State[S, S] { + return G.Get[State[S, S]]() +} + +func Gets[FCT ~func(S) A, A, S any](f FCT) State[S, A] { + return G.Gets[State[S, A]](f) +} + +func Put[S any]() State[S, any] { + return G.Put[State[S, any]]() +} + +func Modify[FCT ~func(S) S, S any](f FCT) State[S, any] { + return G.Modify[State[S, any]](f) +} + +func Of[S, A any](a A) State[S, A] { + return G.Of[State[S, A]](a) +} + +func MonadMap[S any, FCT ~func(A) B, A, B any](fa State[S, A], f FCT) State[S, B] { + return G.MonadMap[State[S, B], State[S, A]](fa, f) +} + +func Map[S any, FCT ~func(A) B, A, B any](f FCT) func(State[S, A]) State[S, B] { + return G.Map[State[S, B], State[S, A]](f) +} + +func MonadChain[S any, FCT ~func(A) State[S, B], A, B any](fa State[S, A], f FCT) State[S, B] { + return G.MonadChain[State[S, B], State[S, A]](fa, f) +} + +func Chain[S any, FCT ~func(A) State[S, B], A, B any](f FCT) func(State[S, A]) State[S, B] { + return G.Chain[State[S, B], State[S, A]](f) +} + +func MonadAp[S, A, B any](fab State[S, func(A) B], fa State[S, A]) State[S, B] { + return G.MonadAp[State[S, B], State[S, func(A) B], State[S, A]](fab, fa) +} + +func Ap[S, A, B any](ga State[S, A]) func(State[S, func(A) B]) State[S, B] { + return G.Ap[State[S, B], State[S, func(A) B], State[S, A]](ga) +} + +func MonadChainFirst[S any, FCT ~func(A) State[S, B], A, B any](ma State[S, A], f FCT) State[S, A] { + return G.MonadChainFirst[State[S, B], State[S, A]](ma, f) +} + +func ChainFirst[S any, FCT ~func(A) State[S, B], A, B any](f FCT) func(State[S, A]) State[S, A] { + return G.ChainFirst[State[S, B], State[S, A]](f) +} + +func Flatten[S, A any](mma State[S, State[S, A]]) State[S, A] { + return G.Flatten[State[S, State[S, A]], State[S, A]](mma) +} + +func Execute[A, S any](s S) func(State[S, A]) S { + return G.Execute[State[S, A]](s) +} + +func Evaluate[A, S any](s S) func(State[S, A]) A { + return G.Evaluate[State[S, A]](s) +} + +func MonadFlap[FAB ~func(A) B, S, A, B any](fab State[S, FAB], a A) State[S, B] { + return G.MonadFlap[FAB, State[S, FAB], State[S, B], S, A, B](fab, a) +} + +func Flap[S, A, B any](a A) func(State[S, func(A) B]) State[S, B] { + return G.Flap[func(A) B, State[S, func(A) B], State[S, B]](a) +} diff --git a/v2/state/testing/laws.go b/v2/state/testing/laws.go new file mode 100644 index 0000000..11e75fc --- /dev/null +++ b/v2/state/testing/laws.go @@ -0,0 +1,78 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" + ST "github.com/IBM/fp-go/v2/state" +) + +// AssertLaws asserts the apply monad laws for the `Either` monad +func AssertLaws[S, A, B, C any](t *testing.T, + eqw EQ.Eq[S], + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, + + s S, +) func(a A) bool { + + fofc := ST.Pointed[S, C]() + fofaa := ST.Pointed[S, func(A) A]() + fofbc := ST.Pointed[S, func(B) C]() + fofabb := ST.Pointed[S, func(func(A) B) B]() + + fmap := ST.Functor[S, func(B) C, func(func(A) B) func(A) C]() + + fapabb := ST.Applicative[S, func(A) B, B]() + fapabac := ST.Applicative[S, func(A) B, func(A) C]() + + maa := ST.Monad[S, A, A]() + mab := ST.Monad[S, A, B]() + mac := ST.Monad[S, A, C]() + mbc := ST.Monad[S, B, C]() + + return L.MonadAssertLaws(t, + ST.Eq(eqw, eqa)(s), + ST.Eq(eqw, eqb)(s), + ST.Eq(eqw, eqc)(s), + + fofc, + fofaa, + fofbc, + fofabb, + + fmap, + + fapabb, + fapabac, + + maa, + mab, + mac, + mbc, + + ab, + bc, + ) + +} diff --git a/v2/state/testing/laws_test.go b/v2/state/testing/laws_test.go new file mode 100644 index 0000000..ed6f45b --- /dev/null +++ b/v2/state/testing/laws_test.go @@ -0,0 +1,49 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + "testing" + + A "github.com/IBM/fp-go/v2/array" + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqs := A.Eq[string](EQ.FromStrictEquals[string]()) + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqs, eqa, eqb, eqc, ab, bc, A.Empty[string]()) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/statereaderioeither/eq.go b/v2/statereaderioeither/eq.go new file mode 100644 index 0000000..4803ab7 --- /dev/null +++ b/v2/statereaderioeither/eq.go @@ -0,0 +1,35 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package statereaderioeither + +import ( + EQ "github.com/IBM/fp-go/v2/eq" + P "github.com/IBM/fp-go/v2/pair" + RIOE "github.com/IBM/fp-go/v2/readerioeither" + G "github.com/IBM/fp-go/v2/statereaderioeither/generic" +) + +// Eq implements the equals predicate for values contained in the [StateReaderIOEither] monad +func Eq[ + S, R, E, A any](eqr EQ.Eq[RIOE.ReaderIOEither[R, E, P.Pair[A, S]]]) func(S) EQ.Eq[StateReaderIOEither[S, R, E, A]] { + return G.Eq[StateReaderIOEither[S, R, E, A]](eqr) +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[ + S, R any, E, A comparable]() func(R) func(S) EQ.Eq[StateReaderIOEither[S, R, E, A]] { + return G.FromStrictEquals[StateReaderIOEither[S, R, E, A]]() +} diff --git a/v2/statereaderioeither/generic/eq.go b/v2/statereaderioeither/generic/eq.go new file mode 100644 index 0000000..477c286 --- /dev/null +++ b/v2/statereaderioeither/generic/eq.go @@ -0,0 +1,49 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + F "github.com/IBM/fp-go/v2/function" + P "github.com/IBM/fp-go/v2/pair" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +// Eq implements the equals predicate for values contained in the [StateReaderIOEither] monad +func Eq[ + SRIOEA ~func(S) RIOEA, + RIOEA ~func(R) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R, E, A any](eqr EQ.Eq[RIOEA]) func(S) EQ.Eq[SRIOEA] { + return func(s S) EQ.Eq[SRIOEA] { + return EQ.FromEquals(func(l, r SRIOEA) bool { + return eqr.Equals(l(s), r(s)) + }) + } +} + +// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function +func FromStrictEquals[ + SRIOEA ~func(S) RIOEA, + RIOEA ~func(R) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R any, E, A comparable]() func(R) func(S) EQ.Eq[SRIOEA] { + return F.Flow2( + G.FromStrictEquals[RIOEA](), + Eq[SRIOEA, RIOEA, IOEA, S, R, E, A], + ) +} diff --git a/v2/statereaderioeither/generic/monad.go b/v2/statereaderioeither/generic/monad.go new file mode 100644 index 0000000..7aca120 --- /dev/null +++ b/v2/statereaderioeither/generic/monad.go @@ -0,0 +1,159 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/internal/applicative" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" + P "github.com/IBM/fp-go/v2/pair" +) + +type stateReaderIOEitherPointed[ + SRIOEA ~func(S) RIOEA, + RIOEA ~func(R) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R, E, A any, +] struct{} + +type stateReaderIOEitherFunctor[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +] struct{} + +type stateReaderIOEitherApplicative[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + SRIOEAB ~func(S) RIOEAB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + RIOEAB ~func(R) IOEAB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEAB ~func() ET.Either[E, P.Pair[func(A) B, S]], + S, R, E, A, B any, +] struct{} + +type stateReaderIOEitherMonad[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + SRIOEAB ~func(S) RIOEAB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + RIOEAB ~func(R) IOEAB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEAB ~func() ET.Either[E, P.Pair[func(A) B, S]], + S, R, E, A, B any, +] struct{} + +func (o *stateReaderIOEitherPointed[SRIOEA, RIOEA, IOEA, S, R, E, A]) Of(a A) SRIOEA { + return Of[SRIOEA](a) +} + +func (o *stateReaderIOEitherMonad[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]) Of(a A) SRIOEA { + return Of[SRIOEA](a) +} + +func (o *stateReaderIOEitherApplicative[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]) Of(a A) SRIOEA { + return Of[SRIOEA](a) +} + +func (o *stateReaderIOEitherMonad[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]) Map(f func(A) B) func(SRIOEA) SRIOEB { + return Map[SRIOEA, SRIOEB](f) +} + +func (o *stateReaderIOEitherApplicative[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]) Map(f func(A) B) func(SRIOEA) SRIOEB { + return Map[SRIOEA, SRIOEB](f) +} + +func (o *stateReaderIOEitherFunctor[SRIOEA, SRIOEB, RIOEA, RIOEB, IOEA, IOEB, S, R, E, A, B]) Map(f func(A) B) func(SRIOEA) SRIOEB { + return Map[SRIOEA, SRIOEB](f) +} + +func (o *stateReaderIOEitherMonad[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]) Chain(f func(A) SRIOEB) func(SRIOEA) SRIOEB { + return Chain[SRIOEA, SRIOEB](f) +} + +func (o *stateReaderIOEitherMonad[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]) Ap(fa SRIOEA) func(SRIOEAB) SRIOEB { + return Ap[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B](fa) +} + +func (o *stateReaderIOEitherApplicative[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]) Ap(fa SRIOEA) func(SRIOEAB) SRIOEB { + return Ap[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B](fa) +} + +// Pointed implements the pointed operations for [StateReaderIOEither] +func Pointed[ + SRIOEA ~func(S) RIOEA, + RIOEA ~func(R) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R, E, A any, +]() pointed.Pointed[A, SRIOEA] { + return &stateReaderIOEitherPointed[SRIOEA, RIOEA, IOEA, S, R, E, A]{} +} + +// Functor implements the functor operations for [StateReaderIOEither] +func Functor[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +]() functor.Functor[A, B, SRIOEA, SRIOEB] { + return &stateReaderIOEitherFunctor[SRIOEA, SRIOEB, RIOEA, RIOEB, IOEA, IOEB, S, R, E, A, B]{} +} + +// Applicative implements the applicative operations for [StateReaderIOEither] +func Applicative[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + SRIOEAB ~func(S) RIOEAB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + RIOEAB ~func(R) IOEAB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEAB ~func() ET.Either[E, P.Pair[func(A) B, S]], + S, R, E, A, B any, +]() applicative.Applicative[A, B, SRIOEA, SRIOEB, SRIOEAB] { + return &stateReaderIOEitherApplicative[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]{} +} + +// Monad implements the monadic operations for [StateReaderIOEither] +func Monad[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + SRIOEAB ~func(S) RIOEAB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + RIOEAB ~func(R) IOEAB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEAB ~func() ET.Either[E, P.Pair[func(A) B, S]], + S, R, E, A, B any, +]() monad.Monad[A, B, SRIOEA, SRIOEB, SRIOEAB] { + return &stateReaderIOEitherMonad[SRIOEA, SRIOEB, SRIOEAB, RIOEA, RIOEB, RIOEAB, IOEA, IOEB, IOEAB, S, R, E, A, B]{} +} diff --git a/v2/statereaderioeither/generic/state.go b/v2/statereaderioeither/generic/state.go new file mode 100644 index 0000000..96be2e5 --- /dev/null +++ b/v2/statereaderioeither/generic/state.go @@ -0,0 +1,425 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + ET "github.com/IBM/fp-go/v2/either" + F "github.com/IBM/fp-go/v2/function" + ST "github.com/IBM/fp-go/v2/internal/statet" + P "github.com/IBM/fp-go/v2/pair" + G "github.com/IBM/fp-go/v2/readerioeither/generic" +) + +func Left[ + SRIOEA ~func(S) RIOEA, + RIOEA ~func(R) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R, E, A any, +](e E) SRIOEA { + return F.Constant1[S](G.Left[RIOEA](e)) +} + +func Right[ + SRIOEA ~func(S) RIOEA, + RIOEA ~func(R) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R, E, A any, +](a A) SRIOEA { + return ST.Of[SRIOEA]( + G.Of[RIOEA], + a, + ) +} + +func Of[ + SRIOEA ~func(S) RIOEA, + RIOEA ~func(R) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R, E, A any, +](a A) SRIOEA { + return Right[SRIOEA](a) +} + +func MonadMap[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](fa SRIOEA, f func(A) B) SRIOEB { + return ST.MonadMap[SRIOEA, SRIOEB]( + G.MonadMap[RIOEA, RIOEB], + fa, + f, + ) +} + +func Map[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](f func(A) B) func(SRIOEA) SRIOEB { + return ST.Map[SRIOEA, SRIOEB]( + G.Map[RIOEA, RIOEB], + f, + ) +} + +func MonadChain[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](fa SRIOEA, f func(A) SRIOEB) SRIOEB { + return ST.MonadChain[SRIOEA, SRIOEB]( + G.MonadChain[RIOEA, RIOEB], + fa, + f, + ) +} + +func Chain[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](f func(A) SRIOEB) func(SRIOEA) SRIOEB { + return ST.Chain[SRIOEA, SRIOEB]( + G.Chain[RIOEA, RIOEB], + f, + ) +} + +func MonadAp[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + SRIOEAB ~func(S) RIOEAB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + RIOEAB ~func(R) IOEAB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEAB ~func() ET.Either[E, P.Pair[func(A) B, S]], + S, R, E, A, B any, +](fab SRIOEAB, fa SRIOEA) SRIOEB { + return ST.MonadAp[SRIOEA, SRIOEB, SRIOEAB]( + G.MonadMap[RIOEA, RIOEB], + G.MonadChain[RIOEAB, RIOEB], + fab, + fa, + ) +} + +func Ap[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + SRIOEAB ~func(S) RIOEAB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + RIOEAB ~func(R) IOEAB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEAB ~func() ET.Either[E, P.Pair[func(A) B, S]], + S, R, E, A, B any, +](fa SRIOEA) func(SRIOEAB) SRIOEB { + return ST.Ap[SRIOEA, SRIOEB, SRIOEAB]( + G.Map[RIOEA, RIOEB], + G.Chain[RIOEAB, RIOEB], + fa, + ) +} + +// Conversions + +func FromReaderIOEither[ + SRIOEA ~func(S) RIOEA, + RIOEA_IN ~func(R) IOEA_IN, + + RIOEA ~func(R) IOEA, + + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEA_IN ~func() ET.Either[E, A], + + S, R, E, A any, +](fa RIOEA_IN) SRIOEA { + return ST.FromF[SRIOEA]( + G.MonadMap[RIOEA_IN, RIOEA], + fa, + ) +} + +func FromReaderEither[ + SRIOEA ~func(S) RIOEA, + RIOEA_IN ~func(R) IOEA_IN, + RIOEA ~func(R) IOEA, + + REA_IN ~func(R) ET.Either[E, A], + + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEA_IN ~func() ET.Either[E, A], + + S, R, E, A any, +](fa REA_IN) SRIOEA { + return FromReaderIOEither[SRIOEA](G.FromReaderEither[REA_IN, RIOEA_IN](fa)) +} + +func FromIOEither[ + SRIOEA ~func(S) RIOEA, + RIOEA_IN ~func(R) IOEA_IN, + IOEA_IN ~func() ET.Either[E, A], + RIOEA ~func(R) IOEA, + + IOEA ~func() ET.Either[E, P.Pair[A, S]], + + S, R, E, A any, +](fa IOEA_IN) SRIOEA { + return FromReaderIOEither[SRIOEA](G.FromIOEither[RIOEA_IN](fa)) +} + +func FromIO[ + SRIOEA ~func(S) RIOEA, + RIOEA_IN ~func(R) IOEA_IN, + + IO_IN ~func() A, + + RIOEA ~func(R) IOEA, + + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEA_IN ~func() ET.Either[E, A], + + S, R, E, A any, +](fa IO_IN) SRIOEA { + return FromReaderIOEither[SRIOEA](G.FromIO[RIOEA_IN](fa)) +} + +func FromReader[ + SRIOEA ~func(S) RIOEA, + RIOEA_IN ~func(R) IOEA_IN, + + R_IN ~func(R) A, + + RIOEA ~func(R) IOEA, + + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEA_IN ~func() ET.Either[E, A], + + S, R, E, A any, +](fa R_IN) SRIOEA { + return FromReaderIOEither[SRIOEA](G.FromReader[R_IN, RIOEA_IN](fa)) +} + +func FromEither[ + SRIOEA ~func(S) RIOEA, + + RIOEA ~func(R) IOEA, + + IOEA ~func() ET.Either[E, P.Pair[A, S]], + + S, R, E, A any, +](ma ET.Either[E, A]) SRIOEA { + return ET.MonadFold(ma, Left[SRIOEA], Right[SRIOEA]) +} + +func FromState[ + SRIOEA ~func(S) RIOEA, + STATE ~func(S) P.Pair[A, S], + RIOEA ~func(R) IOEA, + + IOEA ~func() ET.Either[E, P.Pair[A, S]], + + S, R, E, A any, +](fa STATE) SRIOEA { + return ST.FromState[SRIOEA](G.Of[RIOEA], fa) +} + +// Combinators + +func Local[ + SR1IOEA ~func(S) R1IOEA, + SR2IOEA ~func(S) R2IOEA, + R1IOEA ~func(R1) IOEA, + R2IOEA ~func(R2) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R1, R2, E, A any, +](f func(R2) R1) func(SR1IOEA) SR2IOEA { + return func(ma SR1IOEA) SR2IOEA { + return F.Flow2(ma, G.Local[R1IOEA, R2IOEA](f)) + } +} + +func Asks[ + SRIOEA ~func(S) RIOEA, + RIOEA ~func(R) IOEA, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + S, R, E, A any, +](f func(R) SRIOEA) SRIOEA { + return func(s S) RIOEA { + return func(r R) IOEA { + return f(r)(s)(r) + } + } +} + +func FromIOEitherK[ + SRIOEB ~func(S) RIOEB, + RIOEB_IN ~func(R) IOEB_IN, + IOEB_IN ~func() ET.Either[E, B], + RIOEB ~func(R) IOEB, + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](f func(A) IOEB_IN) func(A) SRIOEB { + return F.Flow2( + f, + FromIOEither[SRIOEB, RIOEB_IN], + ) +} + +func FromEitherK[ + SRIOEB ~func(S) RIOEB, + RIOEB ~func(R) IOEB, + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](f func(A) ET.Either[E, B]) func(A) SRIOEB { + return F.Flow2( + f, + FromEither[SRIOEB], + ) +} + +func FromIOK[ + SRIOEB ~func(S) RIOEB, + RIOEB_IN ~func(R) IOEB_IN, + + IOB_IN ~func() B, + + RIOEB ~func(R) IOEB, + + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEB_IN ~func() ET.Either[E, B], + + S, R, E, A, B any, +](f func(A) IOB_IN) func(A) SRIOEB { + return F.Flow2( + f, + FromIO[SRIOEB, RIOEB_IN, IOB_IN], + ) +} + +func FromReaderIOEitherK[ + SRIOEB ~func(S) RIOEB, + RIOEB_IN ~func(R) IOEB_IN, + IOEB_IN ~func() ET.Either[E, B], + RIOEB ~func(R) IOEB, + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](f func(A) RIOEB_IN) func(A) SRIOEB { + return F.Flow2( + f, + FromReaderIOEither[SRIOEB, RIOEB_IN], + ) +} + +func MonadChainReaderIOEitherK[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEB_IN ~func(R) IOEB_IN, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEB_IN ~func() ET.Either[E, B], + S, R, E, A, B any, +](ma SRIOEA, f func(A) RIOEB_IN) SRIOEB { + return MonadChain(ma, FromReaderIOEitherK[SRIOEB, RIOEB_IN](f)) +} + +func ChainReaderIOEitherK[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEB_IN ~func(R) IOEB_IN, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + IOEB_IN ~func() ET.Either[E, B], + S, R, E, A, B any, +](f func(A) RIOEB_IN) func(SRIOEA) SRIOEB { + return Chain[SRIOEA](FromReaderIOEitherK[SRIOEB, RIOEB_IN](f)) +} + +func MonadChainIOEitherK[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEB_IN ~func(R) IOEB_IN, + IOEB_IN ~func() ET.Either[E, B], + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](ma SRIOEA, f func(A) IOEB_IN) SRIOEB { + return MonadChain(ma, FromIOEitherK[SRIOEB, RIOEB_IN](f)) +} + +func ChainIOEitherK[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEB_IN ~func(R) IOEB_IN, + IOEB_IN ~func() ET.Either[E, B], + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](f func(A) IOEB_IN) func(SRIOEA) SRIOEB { + return Chain[SRIOEA](FromIOEitherK[SRIOEB, RIOEB_IN](f)) +} + +func MonadChainEitherK[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](ma SRIOEA, f func(A) ET.Either[E, B]) SRIOEB { + return MonadChain(ma, FromEitherK[SRIOEB](f)) +} + +func ChainEitherK[ + SRIOEA ~func(S) RIOEA, + SRIOEB ~func(S) RIOEB, + RIOEA ~func(R) IOEA, + RIOEB ~func(R) IOEB, + IOEA ~func() ET.Either[E, P.Pair[A, S]], + IOEB ~func() ET.Either[E, P.Pair[B, S]], + S, R, E, A, B any, +](f func(A) ET.Either[E, B]) func(SRIOEA) SRIOEB { + return Chain[SRIOEA](FromEitherK[SRIOEB](f)) +} diff --git a/v2/statereaderioeither/monad.go b/v2/statereaderioeither/monad.go new file mode 100644 index 0000000..53e8134 --- /dev/null +++ b/v2/statereaderioeither/monad.go @@ -0,0 +1,44 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package statereaderioeither + +import ( + "github.com/IBM/fp-go/v2/internal/applicative" + "github.com/IBM/fp-go/v2/internal/functor" + "github.com/IBM/fp-go/v2/internal/monad" + "github.com/IBM/fp-go/v2/internal/pointed" + G "github.com/IBM/fp-go/v2/statereaderioeither/generic" +) + +// Pointed returns the pointed operations for [StateReaderIOEither] +func Pointed[S, R, E, A any]() pointed.Pointed[A, StateReaderIOEither[S, R, E, A]] { + return G.Pointed[StateReaderIOEither[S, R, E, A]]() +} + +// Functor returns the functor operations for [StateReaderIOEither] +func Functor[S, R, E, A, B any]() functor.Functor[A, B, StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]] { + return G.Functor[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]]() +} + +// Applicative returns the applicative operations for [StateReaderIOEither] +func Applicative[S, R, E, A, B any]() applicative.Applicative[A, B, StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], StateReaderIOEither[S, R, E, func(A) B]] { + return G.Applicative[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], StateReaderIOEither[S, R, E, func(A) B]]() +} + +// Monad returns the monadic operations for [StateReaderIOEither] +func Monad[S, R, E, A, B any]() monad.Monad[A, B, StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], StateReaderIOEither[S, R, E, func(A) B]] { + return G.Monad[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], StateReaderIOEither[S, R, E, func(A) B]]() +} diff --git a/v2/statereaderioeither/state.go b/v2/statereaderioeither/state.go new file mode 100644 index 0000000..40f7ef2 --- /dev/null +++ b/v2/statereaderioeither/state.go @@ -0,0 +1,145 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package statereaderioeither + +import ( + ET "github.com/IBM/fp-go/v2/either" + IO "github.com/IBM/fp-go/v2/io" + IOE "github.com/IBM/fp-go/v2/ioeither" + RD "github.com/IBM/fp-go/v2/reader" + RE "github.com/IBM/fp-go/v2/readereither" + RIOE "github.com/IBM/fp-go/v2/readerioeither" + ST "github.com/IBM/fp-go/v2/state" + G "github.com/IBM/fp-go/v2/statereaderioeither/generic" +) + +func Left[S, R, A, E any](e E) StateReaderIOEither[S, R, E, A] { + return G.Left[StateReaderIOEither[S, R, E, A]](e) +} + +func Right[S, R, E, A any](a A) StateReaderIOEither[S, R, E, A] { + return G.Right[StateReaderIOEither[S, R, E, A]](a) +} + +func Of[S, R, E, A any](a A) StateReaderIOEither[S, R, E, A] { + return G.Of[StateReaderIOEither[S, R, E, A]](a) +} + +func MonadMap[S, R, E, A, B any](fa StateReaderIOEither[S, R, E, A], f func(A) B) StateReaderIOEither[S, R, E, B] { + return G.MonadMap[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]](fa, f) +} + +func Map[S, R, E, A, B any](f func(A) B) func(StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, B] { + return G.Map[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]](f) +} + +func MonadChain[S, R, E, A, B any](fa StateReaderIOEither[S, R, E, A], f func(A) StateReaderIOEither[S, R, E, B]) StateReaderIOEither[S, R, E, B] { + return G.MonadChain[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]](fa, f) +} + +func Chain[S, R, E, A, B any](f func(A) StateReaderIOEither[S, R, E, B]) func(StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, B] { + return G.Chain[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]](f) +} + +func MonadAp[S, R, E, A, B any](fab StateReaderIOEither[S, R, E, func(A) B], fa StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, B] { + return G.MonadAp[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], StateReaderIOEither[S, R, E, func(A) B]](fab, fa) +} + +func Ap[S, R, E, A, B any](fa StateReaderIOEither[S, R, E, A]) func(StateReaderIOEither[S, R, E, func(A) B]) StateReaderIOEither[S, R, E, B] { + return G.Ap[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], StateReaderIOEither[S, R, E, func(A) B]](fa) +} + +func FromReaderIOEither[S, R, E, A any](fa RIOE.ReaderIOEither[R, E, A]) StateReaderIOEither[S, R, E, A] { + return G.FromReaderIOEither[StateReaderIOEither[S, R, E, A]](fa) +} + +func FromReaderEither[S, R, E, A any](fa RE.ReaderEither[R, E, A]) StateReaderIOEither[S, R, E, A] { + return G.FromReaderEither[StateReaderIOEither[S, R, E, A], RIOE.ReaderIOEither[R, E, A]](fa) +} + +func FromIOEither[S, R, E, A any](fa IOE.IOEither[E, A]) StateReaderIOEither[S, R, E, A] { + return G.FromIOEither[StateReaderIOEither[S, R, E, A], RIOE.ReaderIOEither[R, E, A]](fa) +} + +func FromState[S, R, E, A any](sa ST.State[S, A]) StateReaderIOEither[S, R, E, A] { + return G.FromState[StateReaderIOEither[S, R, E, A]](sa) +} + +func FromIO[S, R, E, A any](fa IO.IO[A]) StateReaderIOEither[S, R, E, A] { + return G.FromIO[StateReaderIOEither[S, R, E, A], RIOE.ReaderIOEither[R, E, A]](fa) +} + +func FromReader[S, R, E, A any](fa RD.Reader[R, A]) StateReaderIOEither[S, R, E, A] { + return G.FromReader[StateReaderIOEither[S, R, E, A], RIOE.ReaderIOEither[R, E, A]](fa) +} + +func FromEither[S, R, E, A any](ma ET.Either[E, A]) StateReaderIOEither[S, R, E, A] { + return G.FromEither[StateReaderIOEither[S, R, E, A]](ma) +} + +// Combinators + +func Local[S, R1, R2, E, A, B any](f func(R2) R1) func(StateReaderIOEither[S, R1, E, A]) StateReaderIOEither[S, R2, E, A] { + return G.Local[StateReaderIOEither[S, R1, E, A], StateReaderIOEither[S, R2, E, A]](f) +} + +func Asks[ + S, R, E, A any, +](f func(R) StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, A] { + return G.Asks[StateReaderIOEither[S, R, E, A]](f) +} + +func FromEitherK[S, R, E, A, B any](f func(A) ET.Either[E, B]) func(A) StateReaderIOEither[S, R, E, B] { + return G.FromEitherK[StateReaderIOEither[S, R, E, B]](f) +} + +func FromIOK[S, R, E, A, B any](f func(A) IO.IO[B]) func(A) StateReaderIOEither[S, R, E, B] { + return G.FromIOK[StateReaderIOEither[S, R, E, B], RIOE.ReaderIOEither[R, E, B]](f) +} + +func FromIOEitherK[ + S, R, E, A, B any, +](f func(A) IOE.IOEither[E, B]) func(A) StateReaderIOEither[S, R, E, B] { + return G.FromIOEitherK[StateReaderIOEither[S, R, E, B], RIOE.ReaderIOEither[R, E, B]](f) +} + +func FromReaderIOEitherK[S, R, E, A, B any](f func(A) RIOE.ReaderIOEither[R, E, B]) func(A) StateReaderIOEither[S, R, E, B] { + return G.FromReaderIOEitherK[StateReaderIOEither[S, R, E, B], RIOE.ReaderIOEither[R, E, B]](f) +} + +func MonadChainReaderIOEitherK[S, R, E, A, B any](ma StateReaderIOEither[S, R, E, A], f func(A) RIOE.ReaderIOEither[R, E, B]) StateReaderIOEither[S, R, E, B] { + return G.MonadChainReaderIOEitherK[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]](ma, f) +} + +func ChainReaderIOEitherK[S, R, E, A, B any](f func(A) RIOE.ReaderIOEither[R, E, B]) func(StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, B] { + return G.ChainReaderIOEitherK[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]](f) +} + +func MonadChainIOEitherK[S, R, E, A, B any](ma StateReaderIOEither[S, R, E, A], f func(A) IOE.IOEither[E, B]) StateReaderIOEither[S, R, E, B] { + return G.MonadChainIOEitherK[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], RIOE.ReaderIOEither[R, E, B]](ma, f) +} + +func ChainIOEitherK[S, R, E, A, B any](f func(A) IOE.IOEither[E, B]) func(StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, B] { + return G.ChainIOEitherK[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], RIOE.ReaderIOEither[R, E, B]](f) +} + +func MonadChainEitherK[S, R, E, A, B any](ma StateReaderIOEither[S, R, E, A], f func(A) ET.Either[E, B]) StateReaderIOEither[S, R, E, B] { + return G.MonadChainEitherK[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]](ma, f) +} + +func ChainEitherK[S, R, E, A, B any](f func(A) ET.Either[E, B]) func(StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, B] { + return G.ChainEitherK[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]](f) +} diff --git a/v2/statereaderioeither/testing/laws.go b/v2/statereaderioeither/testing/laws.go new file mode 100644 index 0000000..4bb1f51 --- /dev/null +++ b/v2/statereaderioeither/testing/laws.go @@ -0,0 +1,87 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "testing" + + ET "github.com/IBM/fp-go/v2/either" + EQ "github.com/IBM/fp-go/v2/eq" + L "github.com/IBM/fp-go/v2/internal/monad/testing" + P "github.com/IBM/fp-go/v2/pair" + RIOE "github.com/IBM/fp-go/v2/readerioeither" + ST "github.com/IBM/fp-go/v2/statereaderioeither" +) + +// AssertLaws asserts the apply monad laws for the `Either` monad +func AssertLaws[S, E, R, A, B, C any](t *testing.T, + eqs EQ.Eq[S], + eqe EQ.Eq[E], + eqa EQ.Eq[A], + eqb EQ.Eq[B], + eqc EQ.Eq[C], + + ab func(A) B, + bc func(B) C, + + s S, + r R, +) func(a A) bool { + + eqra := RIOE.Eq[R](ET.Eq(eqe, P.Eq(eqa, eqs)))(r) + eqrb := RIOE.Eq[R](ET.Eq(eqe, P.Eq(eqb, eqs)))(r) + eqrc := RIOE.Eq[R](ET.Eq(eqe, P.Eq(eqc, eqs)))(r) + + fofc := ST.Pointed[S, R, E, C]() + fofaa := ST.Pointed[S, R, E, func(A) A]() + fofbc := ST.Pointed[S, R, E, func(B) C]() + fofabb := ST.Pointed[S, R, E, func(func(A) B) B]() + + fmap := ST.Functor[S, R, E, func(B) C, func(func(A) B) func(A) C]() + + fapabb := ST.Applicative[S, R, E, func(A) B, B]() + fapabac := ST.Applicative[S, R, E, func(A) B, func(A) C]() + + maa := ST.Monad[S, R, E, A, A]() + mab := ST.Monad[S, R, E, A, B]() + mac := ST.Monad[S, R, E, A, C]() + mbc := ST.Monad[S, R, E, B, C]() + + return L.MonadAssertLaws(t, + ST.Eq(eqra)(s), + ST.Eq(eqrb)(s), + ST.Eq(eqrc)(s), + + fofc, + fofaa, + fofbc, + fofabb, + + fmap, + + fapabb, + fapabac, + + maa, + mab, + mac, + mbc, + + ab, + bc, + ) + +} diff --git a/v2/statereaderioeither/testing/laws_test.go b/v2/statereaderioeither/testing/laws_test.go new file mode 100644 index 0000000..1b4a864 --- /dev/null +++ b/v2/statereaderioeither/testing/laws_test.go @@ -0,0 +1,51 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "context" + "fmt" + "testing" + + A "github.com/IBM/fp-go/v2/array" + EQ "github.com/IBM/fp-go/v2/eq" + "github.com/stretchr/testify/assert" +) + +func TestMonadLaws(t *testing.T) { + // some comparison + eqs := A.Eq[string](EQ.FromStrictEquals[string]()) + eqe := EQ.FromStrictEquals[error]() + eqa := EQ.FromStrictEquals[bool]() + eqb := EQ.FromStrictEquals[int]() + eqc := EQ.FromStrictEquals[string]() + + ab := func(a bool) int { + if a { + return 1 + } + return 0 + } + + bc := func(b int) string { + return fmt.Sprintf("value %d", b) + } + + laws := AssertLaws(t, eqs, eqe, eqa, eqb, eqc, ab, bc, A.Empty[string](), context.Background()) + + assert.True(t, laws(true)) + assert.True(t, laws(false)) +} diff --git a/v2/statereaderioeither/type.go b/v2/statereaderioeither/type.go new file mode 100644 index 0000000..1fd4c30 --- /dev/null +++ b/v2/statereaderioeither/type.go @@ -0,0 +1,24 @@ +// Copyright (c) 2024 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package statereaderioeither + +import ( + P "github.com/IBM/fp-go/v2/pair" + RD "github.com/IBM/fp-go/v2/reader" + RIOE "github.com/IBM/fp-go/v2/readerioeither" +) + +type StateReaderIOEither[S, R, E, A any] RD.Reader[S, RIOE.ReaderIOEither[R, E, P.Pair[A, S]]] diff --git a/v2/string/generic/string.go b/v2/string/generic/string.go new file mode 100644 index 0000000..d113aeb --- /dev/null +++ b/v2/string/generic/string.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +// ToBytes converts the string to bytes +func ToBytes[T ~string](s T) []byte { + return []byte(s) +} + +// ToRunes converts the string to runes +func ToRunes[T ~string](s T) []rune { + return []rune(s) +} + +// IsEmpty tests if the string is empty +func IsEmpty[T ~string](s T) bool { + return len(s) == 0 +} + +// IsNonEmpty tests if the string is not empty +func IsNonEmpty[T ~string](s T) bool { + return len(s) > 0 +} + +// Size returns the size of the string +func Size[T ~string](s T) int { + return len(s) +} diff --git a/v2/string/monoid.go b/v2/string/monoid.go new file mode 100644 index 0000000..197fc73 --- /dev/null +++ b/v2/string/monoid.go @@ -0,0 +1,23 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package string + +import ( + M "github.com/IBM/fp-go/v2/monoid" +) + +// Monoid is the monoid implementing string concatenation +var Monoid = M.MakeMonoid(concat, "") diff --git a/v2/string/monoid_test.go b/v2/string/monoid_test.go new file mode 100644 index 0000000..755651d --- /dev/null +++ b/v2/string/monoid_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package string + +import ( + "testing" + + M "github.com/IBM/fp-go/v2/monoid/testing" +) + +func TestMonoid(t *testing.T) { + M.AssertLaws(t, Monoid)([]string{"", "a", "some value"}) +} diff --git a/v2/string/semigroup.go b/v2/string/semigroup.go new file mode 100644 index 0000000..daab28a --- /dev/null +++ b/v2/string/semigroup.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package string + +import ( + "fmt" + + S "github.com/IBM/fp-go/v2/semigroup" +) + +func concat(left string, right string) string { + return fmt.Sprintf("%s%s", left, right) +} + +func Semigroup() S.Semigroup[string] { + return S.MakeSemigroup(concat) +} diff --git a/v2/string/string.go b/v2/string/string.go new file mode 100644 index 0000000..dbb798e --- /dev/null +++ b/v2/string/string.go @@ -0,0 +1,75 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package string + +import ( + "fmt" + "strings" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/ord" +) + +var ( + // ToUpperCase converts the string to uppercase + ToUpperCase = strings.ToUpper + + // ToLowerCase converts the string to lowercase + ToLowerCase = strings.ToLower + + // Ord implements the default ordering for strings + Ord = ord.FromStrictCompare[string]() + + // Join joins strings + Join = F.Curry2(F.Bind2nd[[]string, string, string])(strings.Join) + + // Equals returns a predicate that tests if a string is equal + Equals = F.Curry2(Eq) + + // Includes returns a predicate that tests for the existence of the search string + Includes = F.Curry2(F.Swap(strings.Contains)) +) + +func Eq(left string, right string) bool { + return left == right +} + +func ToBytes(s string) []byte { + return []byte(s) +} + +func ToRunes(s string) []rune { + return []rune(s) +} + +func IsEmpty(s string) bool { + return len(s) == 0 +} + +func IsNonEmpty(s string) bool { + return len(s) > 0 +} + +func Size(s string) int { + return len(s) +} + +// Format applies a format string to an arbitrary value +func Format[T any](format string) func(t T) string { + return func(t T) string { + return fmt.Sprintf(format, t) + } +} diff --git a/v2/string/string_test.go b/v2/string/string_test.go new file mode 100644 index 0000000..46c02e5 --- /dev/null +++ b/v2/string/string_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package string + +import ( + "testing" + + A "github.com/IBM/fp-go/v2/array" + "github.com/stretchr/testify/assert" +) + +func TestEmpty(t *testing.T) { + assert.True(t, IsEmpty("")) + assert.False(t, IsEmpty("Carsten")) +} + +func TestJoin(t *testing.T) { + + x := Join(",")(A.From("a", "b", "c")) + assert.Equal(t, x, x) + + assert.Equal(t, "a,b,c", Join(",")(A.From("a", "b", "c"))) + assert.Equal(t, "a", Join(",")(A.From("a"))) + assert.Equal(t, "", Join(",")(A.Empty[string]())) +} + +func TestEquals(t *testing.T) { + assert.True(t, Equals("a")("a")) + assert.False(t, Equals("a")("b")) + assert.False(t, Equals("b")("a")) +} + +func TestIncludes(t *testing.T) { + assert.True(t, Includes("a")("bab")) + assert.False(t, Includes("bab")("a")) + assert.False(t, Includes("b")("a")) +} diff --git a/v2/tuple/doc.go b/v2/tuple/doc.go new file mode 100644 index 0000000..23ebd35 --- /dev/null +++ b/v2/tuple/doc.go @@ -0,0 +1,18 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tuple + +//go:generate go run .. tuple --count 15 --filename gen.go diff --git a/v2/tuple/gen.go b/v2/tuple/gen.go new file mode 100644 index 0000000..7dce491 --- /dev/null +++ b/v2/tuple/gen.go @@ -0,0 +1,2253 @@ +// Code generated by go generate; DO NOT EDIT. +// This file was generated by robots at +// 2024-02-29 16:19:31.6574369 +0100 CET m=+0.012049601 + +package tuple + +import ( + M "github.com/IBM/fp-go/v2/monoid" + O "github.com/IBM/fp-go/v2/ord" +) + +// Tuple1 is a struct that carries 1 independently typed values +type Tuple1[T1 any] struct { + F1 T1 +} + +// Tuple2 is a struct that carries 2 independently typed values +type Tuple2[T1, T2 any] struct { + F1 T1 + F2 T2 +} + +// Tuple3 is a struct that carries 3 independently typed values +type Tuple3[T1, T2, T3 any] struct { + F1 T1 + F2 T2 + F3 T3 +} + +// Tuple4 is a struct that carries 4 independently typed values +type Tuple4[T1, T2, T3, T4 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 +} + +// Tuple5 is a struct that carries 5 independently typed values +type Tuple5[T1, T2, T3, T4, T5 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 +} + +// Tuple6 is a struct that carries 6 independently typed values +type Tuple6[T1, T2, T3, T4, T5, T6 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 +} + +// Tuple7 is a struct that carries 7 independently typed values +type Tuple7[T1, T2, T3, T4, T5, T6, T7 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 +} + +// Tuple8 is a struct that carries 8 independently typed values +type Tuple8[T1, T2, T3, T4, T5, T6, T7, T8 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 + F8 T8 +} + +// Tuple9 is a struct that carries 9 independently typed values +type Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 + F8 T8 + F9 T9 +} + +// Tuple10 is a struct that carries 10 independently typed values +type Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 + F8 T8 + F9 T9 + F10 T10 +} + +// Tuple11 is a struct that carries 11 independently typed values +type Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 + F8 T8 + F9 T9 + F10 T10 + F11 T11 +} + +// Tuple12 is a struct that carries 12 independently typed values +type Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 + F8 T8 + F9 T9 + F10 T10 + F11 T11 + F12 T12 +} + +// Tuple13 is a struct that carries 13 independently typed values +type Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 + F8 T8 + F9 T9 + F10 T10 + F11 T11 + F12 T12 + F13 T13 +} + +// Tuple14 is a struct that carries 14 independently typed values +type Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 + F8 T8 + F9 T9 + F10 T10 + F11 T11 + F12 T12 + F13 T13 + F14 T14 +} + +// Tuple15 is a struct that carries 15 independently typed values +type Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any] struct { + F1 T1 + F2 T2 + F3 T3 + F4 T4 + F5 T5 + F6 T6 + F7 T7 + F8 T8 + F9 T9 + F10 T10 + F11 T11 + F12 T12 + F13 T13 + F14 T14 + F15 T15 +} + +// MakeTuple1 is a function that converts its 1 parameters into a [Tuple1] +func MakeTuple1[T1 any](t1 T1) Tuple1[T1] { + return Tuple1[T1]{t1} +} + +// Tupled1 converts a function with 1 parameters into a function taking a Tuple1 +// The inverse function is [Untupled1] +func Tupled1[F ~func(T1) R, T1, R any](f F) func(Tuple1[T1]) R { + return func(t Tuple1[T1]) R { + return f(t.F1) + } +} + +// Untupled1 converts a function with a [Tuple1] parameter into a function with 1 parameters +// The inverse function is [Tupled1] +func Untupled1[F ~func(Tuple1[T1]) R, T1, R any](f F) func(T1) R { + return func(t1 T1) R { + return f(MakeTuple1(t1)) + } +} + +// Monoid1 creates a [Monoid] for a [Tuple1] based on 1 monoids for the contained types +func Monoid1[T1 any](m1 M.Monoid[T1]) M.Monoid[Tuple1[T1]] { + return M.MakeMonoid(func(l, r Tuple1[T1]) Tuple1[T1] { + return MakeTuple1(m1.Concat(l.F1, r.F1)) + }, MakeTuple1(m1.Empty())) +} + +// Ord1 creates n [Ord] for a [Tuple1] based on 1 [Ord]s for the contained types +func Ord1[T1 any](o1 O.Ord[T1]) O.Ord[Tuple1[T1]] { + return O.MakeOrd(func(l, r Tuple1[T1]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + return 0 + }, func(l, r Tuple1[T1]) bool { + return o1.Equals(l.F1, r.F1) + }) +} + +// Map1 maps each value of a [Tuple1] via a mapping function +func Map1[F1 ~func(T1) R1, T1, R1 any](f1 F1) func(Tuple1[T1]) Tuple1[R1] { + return func(t Tuple1[T1]) Tuple1[R1] { + return MakeTuple1( + f1(t.F1), + ) + } +} + +// Replicate1 creates a [Tuple1] with all fields set to the input value `t` +func Replicate1[T any](t T) Tuple1[T] { + return MakeTuple1(t) +} + +// String prints some debug info for the [Tuple1] +func (t Tuple1[T1]) String() string { + return tupleString(t.F1) +} + +// MarshalJSON marshals the [Tuple1] into a JSON array +func (t Tuple1[T1]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple1] +func (t *Tuple1[T1]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1) +} + +// ToArray converts the [Tuple1] into an array of type [R] using 1 transformation functions from [T] to [R] +// The inverse function is [FromArray1] +func ToArray1[F1 ~func(T1) R, T1, R any](f1 F1) func(t Tuple1[T1]) []R { + return func(t Tuple1[T1]) []R { + return []R{ + f1(t.F1), + } + } +} + +// FromArray converts an array of [R] into a [Tuple1] using 1 functions from [R] to [T] +// The inverse function is [ToArray1] +func FromArray1[F1 ~func(R) T1, T1, R any](f1 F1) func(r []R) Tuple1[T1] { + return func(r []R) Tuple1[T1] { + return MakeTuple1( + f1(r[0]), + ) + } +} + +// Push1 creates a [Tuple2] from a [Tuple1] by appending a constant value +func Push1[T1, T2 any](value T2) func(Tuple1[T1]) Tuple2[T1, T2] { + return func(t Tuple1[T1]) Tuple2[T1, T2] { + return MakeTuple2(t.F1, value) + } +} + +// MakeTuple2 is a function that converts its 2 parameters into a [Tuple2] +func MakeTuple2[T1, T2 any](t1 T1, t2 T2) Tuple2[T1, T2] { + return Tuple2[T1, T2]{t1, t2} +} + +// Tupled2 converts a function with 2 parameters into a function taking a Tuple2 +// The inverse function is [Untupled2] +func Tupled2[F ~func(T1, T2) R, T1, T2, R any](f F) func(Tuple2[T1, T2]) R { + return func(t Tuple2[T1, T2]) R { + return f(t.F1, t.F2) + } +} + +// Untupled2 converts a function with a [Tuple2] parameter into a function with 2 parameters +// The inverse function is [Tupled2] +func Untupled2[F ~func(Tuple2[T1, T2]) R, T1, T2, R any](f F) func(T1, T2) R { + return func(t1 T1, t2 T2) R { + return f(MakeTuple2(t1, t2)) + } +} + +// Monoid2 creates a [Monoid] for a [Tuple2] based on 2 monoids for the contained types +func Monoid2[T1, T2 any](m1 M.Monoid[T1], m2 M.Monoid[T2]) M.Monoid[Tuple2[T1, T2]] { + return M.MakeMonoid(func(l, r Tuple2[T1, T2]) Tuple2[T1, T2] { + return MakeTuple2(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2)) + }, MakeTuple2(m1.Empty(), m2.Empty())) +} + +// Ord2 creates n [Ord] for a [Tuple2] based on 2 [Ord]s for the contained types +func Ord2[T1, T2 any](o1 O.Ord[T1], o2 O.Ord[T2]) O.Ord[Tuple2[T1, T2]] { + return O.MakeOrd(func(l, r Tuple2[T1, T2]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + return 0 + }, func(l, r Tuple2[T1, T2]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) + }) +} + +// Map2 maps each value of a [Tuple2] via a mapping function +func Map2[F1 ~func(T1) R1, F2 ~func(T2) R2, T1, R1, T2, R2 any](f1 F1, f2 F2) func(Tuple2[T1, T2]) Tuple2[R1, R2] { + return func(t Tuple2[T1, T2]) Tuple2[R1, R2] { + return MakeTuple2( + f1(t.F1), + f2(t.F2), + ) + } +} + +// Replicate2 creates a [Tuple2] with all fields set to the input value `t` +func Replicate2[T any](t T) Tuple2[T, T] { + return MakeTuple2(t, t) +} + +// String prints some debug info for the [Tuple2] +func (t Tuple2[T1, T2]) String() string { + return tupleString(t.F1, t.F2) +} + +// MarshalJSON marshals the [Tuple2] into a JSON array +func (t Tuple2[T1, T2]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple2] +func (t *Tuple2[T1, T2]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2) +} + +// ToArray converts the [Tuple2] into an array of type [R] using 2 transformation functions from [T] to [R] +// The inverse function is [FromArray2] +func ToArray2[F1 ~func(T1) R, F2 ~func(T2) R, T1, T2, R any](f1 F1, f2 F2) func(t Tuple2[T1, T2]) []R { + return func(t Tuple2[T1, T2]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + } + } +} + +// FromArray converts an array of [R] into a [Tuple2] using 2 functions from [R] to [T] +// The inverse function is [ToArray2] +func FromArray2[F1 ~func(R) T1, F2 ~func(R) T2, T1, T2, R any](f1 F1, f2 F2) func(r []R) Tuple2[T1, T2] { + return func(r []R) Tuple2[T1, T2] { + return MakeTuple2( + f1(r[0]), + f2(r[1]), + ) + } +} + +// Push2 creates a [Tuple3] from a [Tuple2] by appending a constant value +func Push2[T1, T2, T3 any](value T3) func(Tuple2[T1, T2]) Tuple3[T1, T2, T3] { + return func(t Tuple2[T1, T2]) Tuple3[T1, T2, T3] { + return MakeTuple3(t.F1, t.F2, value) + } +} + +// MakeTuple3 is a function that converts its 3 parameters into a [Tuple3] +func MakeTuple3[T1, T2, T3 any](t1 T1, t2 T2, t3 T3) Tuple3[T1, T2, T3] { + return Tuple3[T1, T2, T3]{t1, t2, t3} +} + +// Tupled3 converts a function with 3 parameters into a function taking a Tuple3 +// The inverse function is [Untupled3] +func Tupled3[F ~func(T1, T2, T3) R, T1, T2, T3, R any](f F) func(Tuple3[T1, T2, T3]) R { + return func(t Tuple3[T1, T2, T3]) R { + return f(t.F1, t.F2, t.F3) + } +} + +// Untupled3 converts a function with a [Tuple3] parameter into a function with 3 parameters +// The inverse function is [Tupled3] +func Untupled3[F ~func(Tuple3[T1, T2, T3]) R, T1, T2, T3, R any](f F) func(T1, T2, T3) R { + return func(t1 T1, t2 T2, t3 T3) R { + return f(MakeTuple3(t1, t2, t3)) + } +} + +// Monoid3 creates a [Monoid] for a [Tuple3] based on 3 monoids for the contained types +func Monoid3[T1, T2, T3 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3]) M.Monoid[Tuple3[T1, T2, T3]] { + return M.MakeMonoid(func(l, r Tuple3[T1, T2, T3]) Tuple3[T1, T2, T3] { + return MakeTuple3(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3)) + }, MakeTuple3(m1.Empty(), m2.Empty(), m3.Empty())) +} + +// Ord3 creates n [Ord] for a [Tuple3] based on 3 [Ord]s for the contained types +func Ord3[T1, T2, T3 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3]) O.Ord[Tuple3[T1, T2, T3]] { + return O.MakeOrd(func(l, r Tuple3[T1, T2, T3]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + return 0 + }, func(l, r Tuple3[T1, T2, T3]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) + }) +} + +// Map3 maps each value of a [Tuple3] via a mapping function +func Map3[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, T1, R1, T2, R2, T3, R3 any](f1 F1, f2 F2, f3 F3) func(Tuple3[T1, T2, T3]) Tuple3[R1, R2, R3] { + return func(t Tuple3[T1, T2, T3]) Tuple3[R1, R2, R3] { + return MakeTuple3( + f1(t.F1), + f2(t.F2), + f3(t.F3), + ) + } +} + +// Replicate3 creates a [Tuple3] with all fields set to the input value `t` +func Replicate3[T any](t T) Tuple3[T, T, T] { + return MakeTuple3(t, t, t) +} + +// String prints some debug info for the [Tuple3] +func (t Tuple3[T1, T2, T3]) String() string { + return tupleString(t.F1, t.F2, t.F3) +} + +// MarshalJSON marshals the [Tuple3] into a JSON array +func (t Tuple3[T1, T2, T3]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple3] +func (t *Tuple3[T1, T2, T3]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3) +} + +// ToArray converts the [Tuple3] into an array of type [R] using 3 transformation functions from [T] to [R] +// The inverse function is [FromArray3] +func ToArray3[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, T1, T2, T3, R any](f1 F1, f2 F2, f3 F3) func(t Tuple3[T1, T2, T3]) []R { + return func(t Tuple3[T1, T2, T3]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + } + } +} + +// FromArray converts an array of [R] into a [Tuple3] using 3 functions from [R] to [T] +// The inverse function is [ToArray3] +func FromArray3[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, T1, T2, T3, R any](f1 F1, f2 F2, f3 F3) func(r []R) Tuple3[T1, T2, T3] { + return func(r []R) Tuple3[T1, T2, T3] { + return MakeTuple3( + f1(r[0]), + f2(r[1]), + f3(r[2]), + ) + } +} + +// Push3 creates a [Tuple4] from a [Tuple3] by appending a constant value +func Push3[T1, T2, T3, T4 any](value T4) func(Tuple3[T1, T2, T3]) Tuple4[T1, T2, T3, T4] { + return func(t Tuple3[T1, T2, T3]) Tuple4[T1, T2, T3, T4] { + return MakeTuple4(t.F1, t.F2, t.F3, value) + } +} + +// MakeTuple4 is a function that converts its 4 parameters into a [Tuple4] +func MakeTuple4[T1, T2, T3, T4 any](t1 T1, t2 T2, t3 T3, t4 T4) Tuple4[T1, T2, T3, T4] { + return Tuple4[T1, T2, T3, T4]{t1, t2, t3, t4} +} + +// Tupled4 converts a function with 4 parameters into a function taking a Tuple4 +// The inverse function is [Untupled4] +func Tupled4[F ~func(T1, T2, T3, T4) R, T1, T2, T3, T4, R any](f F) func(Tuple4[T1, T2, T3, T4]) R { + return func(t Tuple4[T1, T2, T3, T4]) R { + return f(t.F1, t.F2, t.F3, t.F4) + } +} + +// Untupled4 converts a function with a [Tuple4] parameter into a function with 4 parameters +// The inverse function is [Tupled4] +func Untupled4[F ~func(Tuple4[T1, T2, T3, T4]) R, T1, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4) R { + return f(MakeTuple4(t1, t2, t3, t4)) + } +} + +// Monoid4 creates a [Monoid] for a [Tuple4] based on 4 monoids for the contained types +func Monoid4[T1, T2, T3, T4 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4]) M.Monoid[Tuple4[T1, T2, T3, T4]] { + return M.MakeMonoid(func(l, r Tuple4[T1, T2, T3, T4]) Tuple4[T1, T2, T3, T4] { + return MakeTuple4(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4)) + }, MakeTuple4(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty())) +} + +// Ord4 creates n [Ord] for a [Tuple4] based on 4 [Ord]s for the contained types +func Ord4[T1, T2, T3, T4 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4]) O.Ord[Tuple4[T1, T2, T3, T4]] { + return O.MakeOrd(func(l, r Tuple4[T1, T2, T3, T4]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + return 0 + }, func(l, r Tuple4[T1, T2, T3, T4]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) + }) +} + +// Map4 maps each value of a [Tuple4] via a mapping function +func Map4[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, T1, R1, T2, R2, T3, R3, T4, R4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(Tuple4[T1, T2, T3, T4]) Tuple4[R1, R2, R3, R4] { + return func(t Tuple4[T1, T2, T3, T4]) Tuple4[R1, R2, R3, R4] { + return MakeTuple4( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + ) + } +} + +// Replicate4 creates a [Tuple4] with all fields set to the input value `t` +func Replicate4[T any](t T) Tuple4[T, T, T, T] { + return MakeTuple4(t, t, t, t) +} + +// String prints some debug info for the [Tuple4] +func (t Tuple4[T1, T2, T3, T4]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4) +} + +// MarshalJSON marshals the [Tuple4] into a JSON array +func (t Tuple4[T1, T2, T3, T4]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple4] +func (t *Tuple4[T1, T2, T3, T4]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4) +} + +// ToArray converts the [Tuple4] into an array of type [R] using 4 transformation functions from [T] to [R] +// The inverse function is [FromArray4] +func ToArray4[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, T1, T2, T3, T4, R any](f1 F1, f2 F2, f3 F3, f4 F4) func(t Tuple4[T1, T2, T3, T4]) []R { + return func(t Tuple4[T1, T2, T3, T4]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + } + } +} + +// FromArray converts an array of [R] into a [Tuple4] using 4 functions from [R] to [T] +// The inverse function is [ToArray4] +func FromArray4[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, T1, T2, T3, T4, R any](f1 F1, f2 F2, f3 F3, f4 F4) func(r []R) Tuple4[T1, T2, T3, T4] { + return func(r []R) Tuple4[T1, T2, T3, T4] { + return MakeTuple4( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + ) + } +} + +// Push4 creates a [Tuple5] from a [Tuple4] by appending a constant value +func Push4[T1, T2, T3, T4, T5 any](value T5) func(Tuple4[T1, T2, T3, T4]) Tuple5[T1, T2, T3, T4, T5] { + return func(t Tuple4[T1, T2, T3, T4]) Tuple5[T1, T2, T3, T4, T5] { + return MakeTuple5(t.F1, t.F2, t.F3, t.F4, value) + } +} + +// MakeTuple5 is a function that converts its 5 parameters into a [Tuple5] +func MakeTuple5[T1, T2, T3, T4, T5 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Tuple5[T1, T2, T3, T4, T5] { + return Tuple5[T1, T2, T3, T4, T5]{t1, t2, t3, t4, t5} +} + +// Tupled5 converts a function with 5 parameters into a function taking a Tuple5 +// The inverse function is [Untupled5] +func Tupled5[F ~func(T1, T2, T3, T4, T5) R, T1, T2, T3, T4, T5, R any](f F) func(Tuple5[T1, T2, T3, T4, T5]) R { + return func(t Tuple5[T1, T2, T3, T4, T5]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5) + } +} + +// Untupled5 converts a function with a [Tuple5] parameter into a function with 5 parameters +// The inverse function is [Tupled5] +func Untupled5[F ~func(Tuple5[T1, T2, T3, T4, T5]) R, T1, T2, T3, T4, T5, R any](f F) func(T1, T2, T3, T4, T5) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) R { + return f(MakeTuple5(t1, t2, t3, t4, t5)) + } +} + +// Monoid5 creates a [Monoid] for a [Tuple5] based on 5 monoids for the contained types +func Monoid5[T1, T2, T3, T4, T5 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5]) M.Monoid[Tuple5[T1, T2, T3, T4, T5]] { + return M.MakeMonoid(func(l, r Tuple5[T1, T2, T3, T4, T5]) Tuple5[T1, T2, T3, T4, T5] { + return MakeTuple5(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5)) + }, MakeTuple5(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty())) +} + +// Ord5 creates n [Ord] for a [Tuple5] based on 5 [Ord]s for the contained types +func Ord5[T1, T2, T3, T4, T5 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5]) O.Ord[Tuple5[T1, T2, T3, T4, T5]] { + return O.MakeOrd(func(l, r Tuple5[T1, T2, T3, T4, T5]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + return 0 + }, func(l, r Tuple5[T1, T2, T3, T4, T5]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) + }) +} + +// Map5 maps each value of a [Tuple5] via a mapping function +func Map5[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(Tuple5[T1, T2, T3, T4, T5]) Tuple5[R1, R2, R3, R4, R5] { + return func(t Tuple5[T1, T2, T3, T4, T5]) Tuple5[R1, R2, R3, R4, R5] { + return MakeTuple5( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + ) + } +} + +// Replicate5 creates a [Tuple5] with all fields set to the input value `t` +func Replicate5[T any](t T) Tuple5[T, T, T, T, T] { + return MakeTuple5(t, t, t, t, t) +} + +// String prints some debug info for the [Tuple5] +func (t Tuple5[T1, T2, T3, T4, T5]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5) +} + +// MarshalJSON marshals the [Tuple5] into a JSON array +func (t Tuple5[T1, T2, T3, T4, T5]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple5] +func (t *Tuple5[T1, T2, T3, T4, T5]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5) +} + +// ToArray converts the [Tuple5] into an array of type [R] using 5 transformation functions from [T] to [R] +// The inverse function is [FromArray5] +func ToArray5[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, T1, T2, T3, T4, T5, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(t Tuple5[T1, T2, T3, T4, T5]) []R { + return func(t Tuple5[T1, T2, T3, T4, T5]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + } + } +} + +// FromArray converts an array of [R] into a [Tuple5] using 5 functions from [R] to [T] +// The inverse function is [ToArray5] +func FromArray5[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, T1, T2, T3, T4, T5, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(r []R) Tuple5[T1, T2, T3, T4, T5] { + return func(r []R) Tuple5[T1, T2, T3, T4, T5] { + return MakeTuple5( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + ) + } +} + +// Push5 creates a [Tuple6] from a [Tuple5] by appending a constant value +func Push5[T1, T2, T3, T4, T5, T6 any](value T6) func(Tuple5[T1, T2, T3, T4, T5]) Tuple6[T1, T2, T3, T4, T5, T6] { + return func(t Tuple5[T1, T2, T3, T4, T5]) Tuple6[T1, T2, T3, T4, T5, T6] { + return MakeTuple6(t.F1, t.F2, t.F3, t.F4, t.F5, value) + } +} + +// MakeTuple6 is a function that converts its 6 parameters into a [Tuple6] +func MakeTuple6[T1, T2, T3, T4, T5, T6 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Tuple6[T1, T2, T3, T4, T5, T6] { + return Tuple6[T1, T2, T3, T4, T5, T6]{t1, t2, t3, t4, t5, t6} +} + +// Tupled6 converts a function with 6 parameters into a function taking a Tuple6 +// The inverse function is [Untupled6] +func Tupled6[F ~func(T1, T2, T3, T4, T5, T6) R, T1, T2, T3, T4, T5, T6, R any](f F) func(Tuple6[T1, T2, T3, T4, T5, T6]) R { + return func(t Tuple6[T1, T2, T3, T4, T5, T6]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6) + } +} + +// Untupled6 converts a function with a [Tuple6] parameter into a function with 6 parameters +// The inverse function is [Tupled6] +func Untupled6[F ~func(Tuple6[T1, T2, T3, T4, T5, T6]) R, T1, T2, T3, T4, T5, T6, R any](f F) func(T1, T2, T3, T4, T5, T6) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) R { + return f(MakeTuple6(t1, t2, t3, t4, t5, t6)) + } +} + +// Monoid6 creates a [Monoid] for a [Tuple6] based on 6 monoids for the contained types +func Monoid6[T1, T2, T3, T4, T5, T6 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6]) M.Monoid[Tuple6[T1, T2, T3, T4, T5, T6]] { + return M.MakeMonoid(func(l, r Tuple6[T1, T2, T3, T4, T5, T6]) Tuple6[T1, T2, T3, T4, T5, T6] { + return MakeTuple6(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6)) + }, MakeTuple6(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty())) +} + +// Ord6 creates n [Ord] for a [Tuple6] based on 6 [Ord]s for the contained types +func Ord6[T1, T2, T3, T4, T5, T6 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6]) O.Ord[Tuple6[T1, T2, T3, T4, T5, T6]] { + return O.MakeOrd(func(l, r Tuple6[T1, T2, T3, T4, T5, T6]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + return 0 + }, func(l, r Tuple6[T1, T2, T3, T4, T5, T6]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) + }) +} + +// Map6 maps each value of a [Tuple6] via a mapping function +func Map6[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(Tuple6[T1, T2, T3, T4, T5, T6]) Tuple6[R1, R2, R3, R4, R5, R6] { + return func(t Tuple6[T1, T2, T3, T4, T5, T6]) Tuple6[R1, R2, R3, R4, R5, R6] { + return MakeTuple6( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + ) + } +} + +// Replicate6 creates a [Tuple6] with all fields set to the input value `t` +func Replicate6[T any](t T) Tuple6[T, T, T, T, T, T] { + return MakeTuple6(t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple6] +func (t Tuple6[T1, T2, T3, T4, T5, T6]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6) +} + +// MarshalJSON marshals the [Tuple6] into a JSON array +func (t Tuple6[T1, T2, T3, T4, T5, T6]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple6] +func (t *Tuple6[T1, T2, T3, T4, T5, T6]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6) +} + +// ToArray converts the [Tuple6] into an array of type [R] using 6 transformation functions from [T] to [R] +// The inverse function is [FromArray6] +func ToArray6[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, T1, T2, T3, T4, T5, T6, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(t Tuple6[T1, T2, T3, T4, T5, T6]) []R { + return func(t Tuple6[T1, T2, T3, T4, T5, T6]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + } + } +} + +// FromArray converts an array of [R] into a [Tuple6] using 6 functions from [R] to [T] +// The inverse function is [ToArray6] +func FromArray6[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, T1, T2, T3, T4, T5, T6, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(r []R) Tuple6[T1, T2, T3, T4, T5, T6] { + return func(r []R) Tuple6[T1, T2, T3, T4, T5, T6] { + return MakeTuple6( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + ) + } +} + +// Push6 creates a [Tuple7] from a [Tuple6] by appending a constant value +func Push6[T1, T2, T3, T4, T5, T6, T7 any](value T7) func(Tuple6[T1, T2, T3, T4, T5, T6]) Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return func(t Tuple6[T1, T2, T3, T4, T5, T6]) Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return MakeTuple7(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, value) + } +} + +// MakeTuple7 is a function that converts its 7 parameters into a [Tuple7] +func MakeTuple7[T1, T2, T3, T4, T5, T6, T7 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return Tuple7[T1, T2, T3, T4, T5, T6, T7]{t1, t2, t3, t4, t5, t6, t7} +} + +// Tupled7 converts a function with 7 parameters into a function taking a Tuple7 +// The inverse function is [Untupled7] +func Tupled7[F ~func(T1, T2, T3, T4, T5, T6, T7) R, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(Tuple7[T1, T2, T3, T4, T5, T6, T7]) R { + return func(t Tuple7[T1, T2, T3, T4, T5, T6, T7]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7) + } +} + +// Untupled7 converts a function with a [Tuple7] parameter into a function with 7 parameters +// The inverse function is [Tupled7] +func Untupled7[F ~func(Tuple7[T1, T2, T3, T4, T5, T6, T7]) R, T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T1, T2, T3, T4, T5, T6, T7) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) R { + return f(MakeTuple7(t1, t2, t3, t4, t5, t6, t7)) + } +} + +// Monoid7 creates a [Monoid] for a [Tuple7] based on 7 monoids for the contained types +func Monoid7[T1, T2, T3, T4, T5, T6, T7 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7]) M.Monoid[Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return M.MakeMonoid(func(l, r Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return MakeTuple7(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7)) + }, MakeTuple7(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty())) +} + +// Ord7 creates n [Ord] for a [Tuple7] based on 7 [Ord]s for the contained types +func Ord7[T1, T2, T3, T4, T5, T6, T7 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7]) O.Ord[Tuple7[T1, T2, T3, T4, T5, T6, T7]] { + return O.MakeOrd(func(l, r Tuple7[T1, T2, T3, T4, T5, T6, T7]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + return 0 + }, func(l, r Tuple7[T1, T2, T3, T4, T5, T6, T7]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) + }) +} + +// Map7 maps each value of a [Tuple7] via a mapping function +func Map7[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple7[R1, R2, R3, R4, R5, R6, R7] { + return func(t Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple7[R1, R2, R3, R4, R5, R6, R7] { + return MakeTuple7( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + ) + } +} + +// Replicate7 creates a [Tuple7] with all fields set to the input value `t` +func Replicate7[T any](t T) Tuple7[T, T, T, T, T, T, T] { + return MakeTuple7(t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple7] +func (t Tuple7[T1, T2, T3, T4, T5, T6, T7]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7) +} + +// MarshalJSON marshals the [Tuple7] into a JSON array +func (t Tuple7[T1, T2, T3, T4, T5, T6, T7]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple7] +func (t *Tuple7[T1, T2, T3, T4, T5, T6, T7]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7) +} + +// ToArray converts the [Tuple7] into an array of type [R] using 7 transformation functions from [T] to [R] +// The inverse function is [FromArray7] +func ToArray7[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, T1, T2, T3, T4, T5, T6, T7, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(t Tuple7[T1, T2, T3, T4, T5, T6, T7]) []R { + return func(t Tuple7[T1, T2, T3, T4, T5, T6, T7]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + } + } +} + +// FromArray converts an array of [R] into a [Tuple7] using 7 functions from [R] to [T] +// The inverse function is [ToArray7] +func FromArray7[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, T1, T2, T3, T4, T5, T6, T7, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(r []R) Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return func(r []R) Tuple7[T1, T2, T3, T4, T5, T6, T7] { + return MakeTuple7( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + ) + } +} + +// Push7 creates a [Tuple8] from a [Tuple7] by appending a constant value +func Push7[T1, T2, T3, T4, T5, T6, T7, T8 any](value T8) func(Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return func(t Tuple7[T1, T2, T3, T4, T5, T6, T7]) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return MakeTuple8(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, value) + } +} + +// MakeTuple8 is a function that converts its 8 parameters into a [Tuple8] +func MakeTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]{t1, t2, t3, t4, t5, t6, t7, t8} +} + +// Tupled8 converts a function with 8 parameters into a function taking a Tuple8 +// The inverse function is [Untupled8] +func Tupled8[F ~func(T1, T2, T3, T4, T5, T6, T7, T8) R, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) R { + return func(t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8) + } +} + +// Untupled8 converts a function with a [Tuple8] parameter into a function with 8 parameters +// The inverse function is [Tupled8] +func Untupled8[F ~func(Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) R, T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) R { + return f(MakeTuple8(t1, t2, t3, t4, t5, t6, t7, t8)) + } +} + +// Monoid8 creates a [Monoid] for a [Tuple8] based on 8 monoids for the contained types +func Monoid8[T1, T2, T3, T4, T5, T6, T7, T8 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7], m8 M.Monoid[T8]) M.Monoid[Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return M.MakeMonoid(func(l, r Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return MakeTuple8(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7), m8.Concat(l.F8, r.F8)) + }, MakeTuple8(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty(), m8.Empty())) +} + +// Ord8 creates n [Ord] for a [Tuple8] based on 8 [Ord]s for the contained types +func Ord8[T1, T2, T3, T4, T5, T6, T7, T8 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8]) O.Ord[Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] { + return O.MakeOrd(func(l, r Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + if c := o8.Compare(l.F8, r.F8); c != 0 { + return c + } + return 0 + }, func(l, r Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) + }) +} + +// Map8 maps each value of a [Tuple8] via a mapping function +func Map8[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, F8 ~func(T8) R8, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7, T8, R8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple8[R1, R2, R3, R4, R5, R6, R7, R8] { + return func(t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple8[R1, R2, R3, R4, R5, R6, R7, R8] { + return MakeTuple8( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + ) + } +} + +// Replicate8 creates a [Tuple8] with all fields set to the input value `t` +func Replicate8[T any](t T) Tuple8[T, T, T, T, T, T, T, T] { + return MakeTuple8(t, t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple8] +func (t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8) +} + +// MarshalJSON marshals the [Tuple8] into a JSON array +func (t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple8] +func (t *Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7, &t.F8) +} + +// ToArray converts the [Tuple8] into an array of type [R] using 8 transformation functions from [T] to [R] +// The inverse function is [FromArray8] +func ToArray8[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, F8 ~func(T8) R, T1, T2, T3, T4, T5, T6, T7, T8, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) []R { + return func(t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + } + } +} + +// FromArray converts an array of [R] into a [Tuple8] using 8 functions from [R] to [T] +// The inverse function is [ToArray8] +func FromArray8[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, F8 ~func(R) T8, T1, T2, T3, T4, T5, T6, T7, T8, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(r []R) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return func(r []R) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] { + return MakeTuple8( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + f8(r[7]), + ) + } +} + +// Push8 creates a [Tuple9] from a [Tuple8] by appending a constant value +func Push8[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](value T9) func(Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return func(t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return MakeTuple9(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, value) + } +} + +// MakeTuple9 is a function that converts its 9 parameters into a [Tuple9] +func MakeTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]{t1, t2, t3, t4, t5, t6, t7, t8, t9} +} + +// Tupled9 converts a function with 9 parameters into a function taking a Tuple9 +// The inverse function is [Untupled9] +func Tupled9[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) R { + return func(t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9) + } +} + +// Untupled9 converts a function with a [Tuple9] parameter into a function with 9 parameters +// The inverse function is [Tupled9] +func Untupled9[F ~func(Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) R { + return f(MakeTuple9(t1, t2, t3, t4, t5, t6, t7, t8, t9)) + } +} + +// Monoid9 creates a [Monoid] for a [Tuple9] based on 9 monoids for the contained types +func Monoid9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7], m8 M.Monoid[T8], m9 M.Monoid[T9]) M.Monoid[Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return M.MakeMonoid(func(l, r Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return MakeTuple9(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7), m8.Concat(l.F8, r.F8), m9.Concat(l.F9, r.F9)) + }, MakeTuple9(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty(), m8.Empty(), m9.Empty())) +} + +// Ord9 creates n [Ord] for a [Tuple9] based on 9 [Ord]s for the contained types +func Ord9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9]) O.Ord[Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] { + return O.MakeOrd(func(l, r Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + if c := o8.Compare(l.F8, r.F8); c != 0 { + return c + } + if c := o9.Compare(l.F9, r.F9); c != 0 { + return c + } + return 0 + }, func(l, r Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9) + }) +} + +// Map9 maps each value of a [Tuple9] via a mapping function +func Map9[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, F8 ~func(T8) R8, F9 ~func(T9) R9, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7, T8, R8, T9, R9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple9[R1, R2, R3, R4, R5, R6, R7, R8, R9] { + return func(t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple9[R1, R2, R3, R4, R5, R6, R7, R8, R9] { + return MakeTuple9( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + ) + } +} + +// Replicate9 creates a [Tuple9] with all fields set to the input value `t` +func Replicate9[T any](t T) Tuple9[T, T, T, T, T, T, T, T, T] { + return MakeTuple9(t, t, t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple9] +func (t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9) +} + +// MarshalJSON marshals the [Tuple9] into a JSON array +func (t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple9] +func (t *Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7, &t.F8, &t.F9) +} + +// ToArray converts the [Tuple9] into an array of type [R] using 9 transformation functions from [T] to [R] +// The inverse function is [FromArray9] +func ToArray9[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, F8 ~func(T8) R, F9 ~func(T9) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) []R { + return func(t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + } + } +} + +// FromArray converts an array of [R] into a [Tuple9] using 9 functions from [R] to [T] +// The inverse function is [ToArray9] +func FromArray9[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, F8 ~func(R) T8, F9 ~func(R) T9, T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(r []R) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return func(r []R) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { + return MakeTuple9( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + f8(r[7]), + f9(r[8]), + ) + } +} + +// Push9 creates a [Tuple10] from a [Tuple9] by appending a constant value +func Push9[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](value T10) func(Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return func(t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return MakeTuple10(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, value) + } +} + +// MakeTuple10 is a function that converts its 10 parameters into a [Tuple10] +func MakeTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10} +} + +// Tupled10 converts a function with 10 parameters into a function taking a Tuple10 +// The inverse function is [Untupled10] +func Tupled10[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) R { + return func(t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10) + } +} + +// Untupled10 converts a function with a [Tuple10] parameter into a function with 10 parameters +// The inverse function is [Tupled10] +func Untupled10[F ~func(Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) R { + return f(MakeTuple10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)) + } +} + +// Monoid10 creates a [Monoid] for a [Tuple10] based on 10 monoids for the contained types +func Monoid10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7], m8 M.Monoid[T8], m9 M.Monoid[T9], m10 M.Monoid[T10]) M.Monoid[Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return M.MakeMonoid(func(l, r Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return MakeTuple10(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7), m8.Concat(l.F8, r.F8), m9.Concat(l.F9, r.F9), m10.Concat(l.F10, r.F10)) + }, MakeTuple10(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty(), m8.Empty(), m9.Empty(), m10.Empty())) +} + +// Ord10 creates n [Ord] for a [Tuple10] based on 10 [Ord]s for the contained types +func Ord10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9], o10 O.Ord[T10]) O.Ord[Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] { + return O.MakeOrd(func(l, r Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + if c := o8.Compare(l.F8, r.F8); c != 0 { + return c + } + if c := o9.Compare(l.F9, r.F9); c != 0 { + return c + } + if c := o10.Compare(l.F10, r.F10); c != 0 { + return c + } + return 0 + }, func(l, r Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9) && o10.Equals(l.F10, r.F10) + }) +} + +// Map10 maps each value of a [Tuple10] via a mapping function +func Map10[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, F8 ~func(T8) R8, F9 ~func(T9) R9, F10 ~func(T10) R10, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7, T8, R8, T9, R9, T10, R10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) Tuple10[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10] { + return func(t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) Tuple10[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10] { + return MakeTuple10( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + ) + } +} + +// Replicate10 creates a [Tuple10] with all fields set to the input value `t` +func Replicate10[T any](t T) Tuple10[T, T, T, T, T, T, T, T, T, T] { + return MakeTuple10(t, t, t, t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple10] +func (t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10) +} + +// MarshalJSON marshals the [Tuple10] into a JSON array +func (t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple10] +func (t *Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7, &t.F8, &t.F9, &t.F10) +} + +// ToArray converts the [Tuple10] into an array of type [R] using 10 transformation functions from [T] to [R] +// The inverse function is [FromArray10] +func ToArray10[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, F8 ~func(T8) R, F9 ~func(T9) R, F10 ~func(T10) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) []R { + return func(t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + } + } +} + +// FromArray converts an array of [R] into a [Tuple10] using 10 functions from [R] to [T] +// The inverse function is [ToArray10] +func FromArray10[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, F8 ~func(R) T8, F9 ~func(R) T9, F10 ~func(R) T10, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(r []R) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return func(r []R) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { + return MakeTuple10( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + f8(r[7]), + f9(r[8]), + f10(r[9]), + ) + } +} + +// Push10 creates a [Tuple11] from a [Tuple10] by appending a constant value +func Push10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](value T11) func(Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { + return func(t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { + return MakeTuple11(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, value) + } +} + +// MakeTuple11 is a function that converts its 11 parameters into a [Tuple11] +func MakeTuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { + return Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11} +} + +// Tupled11 converts a function with 11 parameters into a function taking a Tuple11 +// The inverse function is [Untupled11] +func Tupled11[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R any](f F) func(Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) R { + return func(t Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11) + } +} + +// Untupled11 converts a function with a [Tuple11] parameter into a function with 11 parameters +// The inverse function is [Tupled11] +func Untupled11[F ~func(Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) R { + return f(MakeTuple11(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)) + } +} + +// Monoid11 creates a [Monoid] for a [Tuple11] based on 11 monoids for the contained types +func Monoid11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7], m8 M.Monoid[T8], m9 M.Monoid[T9], m10 M.Monoid[T10], m11 M.Monoid[T11]) M.Monoid[Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] { + return M.MakeMonoid(func(l, r Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { + return MakeTuple11(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7), m8.Concat(l.F8, r.F8), m9.Concat(l.F9, r.F9), m10.Concat(l.F10, r.F10), m11.Concat(l.F11, r.F11)) + }, MakeTuple11(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty(), m8.Empty(), m9.Empty(), m10.Empty(), m11.Empty())) +} + +// Ord11 creates n [Ord] for a [Tuple11] based on 11 [Ord]s for the contained types +func Ord11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9], o10 O.Ord[T10], o11 O.Ord[T11]) O.Ord[Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]] { + return O.MakeOrd(func(l, r Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + if c := o8.Compare(l.F8, r.F8); c != 0 { + return c + } + if c := o9.Compare(l.F9, r.F9); c != 0 { + return c + } + if c := o10.Compare(l.F10, r.F10); c != 0 { + return c + } + if c := o11.Compare(l.F11, r.F11); c != 0 { + return c + } + return 0 + }, func(l, r Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9) && o10.Equals(l.F10, r.F10) && o11.Equals(l.F11, r.F11) + }) +} + +// Map11 maps each value of a [Tuple11] via a mapping function +func Map11[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, F8 ~func(T8) R8, F9 ~func(T9) R9, F10 ~func(T10) R10, F11 ~func(T11) R11, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7, T8, R8, T9, R9, T10, R10, T11, R11 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) func(Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) Tuple11[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11] { + return func(t Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) Tuple11[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11] { + return MakeTuple11( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + ) + } +} + +// Replicate11 creates a [Tuple11] with all fields set to the input value `t` +func Replicate11[T any](t T) Tuple11[T, T, T, T, T, T, T, T, T, T, T] { + return MakeTuple11(t, t, t, t, t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple11] +func (t Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11) +} + +// MarshalJSON marshals the [Tuple11] into a JSON array +func (t Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple11] +func (t *Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7, &t.F8, &t.F9, &t.F10, &t.F11) +} + +// ToArray converts the [Tuple11] into an array of type [R] using 11 transformation functions from [T] to [R] +// The inverse function is [FromArray11] +func ToArray11[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, F8 ~func(T8) R, F9 ~func(T9) R, F10 ~func(T10) R, F11 ~func(T11) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) func(t Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) []R { + return func(t Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + } + } +} + +// FromArray converts an array of [R] into a [Tuple11] using 11 functions from [R] to [T] +// The inverse function is [ToArray11] +func FromArray11[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, F8 ~func(R) T8, F9 ~func(R) T9, F10 ~func(R) T10, F11 ~func(R) T11, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11) func(r []R) Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { + return func(r []R) Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { + return MakeTuple11( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + f8(r[7]), + f9(r[8]), + f10(r[9]), + f11(r[10]), + ) + } +} + +// Push11 creates a [Tuple12] from a [Tuple11] by appending a constant value +func Push11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](value T12) func(Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { + return func(t Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { + return MakeTuple12(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, value) + } +} + +// MakeTuple12 is a function that converts its 12 parameters into a [Tuple12] +func MakeTuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { + return Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12} +} + +// Tupled12 converts a function with 12 parameters into a function taking a Tuple12 +// The inverse function is [Untupled12] +func Tupled12[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R any](f F) func(Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) R { + return func(t Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12) + } +} + +// Untupled12 converts a function with a [Tuple12] parameter into a function with 12 parameters +// The inverse function is [Tupled12] +func Untupled12[F ~func(Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) R { + return f(MakeTuple12(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12)) + } +} + +// Monoid12 creates a [Monoid] for a [Tuple12] based on 12 monoids for the contained types +func Monoid12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7], m8 M.Monoid[T8], m9 M.Monoid[T9], m10 M.Monoid[T10], m11 M.Monoid[T11], m12 M.Monoid[T12]) M.Monoid[Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] { + return M.MakeMonoid(func(l, r Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { + return MakeTuple12(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7), m8.Concat(l.F8, r.F8), m9.Concat(l.F9, r.F9), m10.Concat(l.F10, r.F10), m11.Concat(l.F11, r.F11), m12.Concat(l.F12, r.F12)) + }, MakeTuple12(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty(), m8.Empty(), m9.Empty(), m10.Empty(), m11.Empty(), m12.Empty())) +} + +// Ord12 creates n [Ord] for a [Tuple12] based on 12 [Ord]s for the contained types +func Ord12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9], o10 O.Ord[T10], o11 O.Ord[T11], o12 O.Ord[T12]) O.Ord[Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]] { + return O.MakeOrd(func(l, r Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + if c := o8.Compare(l.F8, r.F8); c != 0 { + return c + } + if c := o9.Compare(l.F9, r.F9); c != 0 { + return c + } + if c := o10.Compare(l.F10, r.F10); c != 0 { + return c + } + if c := o11.Compare(l.F11, r.F11); c != 0 { + return c + } + if c := o12.Compare(l.F12, r.F12); c != 0 { + return c + } + return 0 + }, func(l, r Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9) && o10.Equals(l.F10, r.F10) && o11.Equals(l.F11, r.F11) && o12.Equals(l.F12, r.F12) + }) +} + +// Map12 maps each value of a [Tuple12] via a mapping function +func Map12[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, F8 ~func(T8) R8, F9 ~func(T9) R9, F10 ~func(T10) R10, F11 ~func(T11) R11, F12 ~func(T12) R12, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7, T8, R8, T9, R9, T10, R10, T11, R11, T12, R12 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) func(Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) Tuple12[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12] { + return func(t Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) Tuple12[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12] { + return MakeTuple12( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + f12(t.F12), + ) + } +} + +// Replicate12 creates a [Tuple12] with all fields set to the input value `t` +func Replicate12[T any](t T) Tuple12[T, T, T, T, T, T, T, T, T, T, T, T] { + return MakeTuple12(t, t, t, t, t, t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple12] +func (t Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12) +} + +// MarshalJSON marshals the [Tuple12] into a JSON array +func (t Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple12] +func (t *Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7, &t.F8, &t.F9, &t.F10, &t.F11, &t.F12) +} + +// ToArray converts the [Tuple12] into an array of type [R] using 12 transformation functions from [T] to [R] +// The inverse function is [FromArray12] +func ToArray12[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, F8 ~func(T8) R, F9 ~func(T9) R, F10 ~func(T10) R, F11 ~func(T11) R, F12 ~func(T12) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) func(t Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) []R { + return func(t Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + f12(t.F12), + } + } +} + +// FromArray converts an array of [R] into a [Tuple12] using 12 functions from [R] to [T] +// The inverse function is [ToArray12] +func FromArray12[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, F8 ~func(R) T8, F9 ~func(R) T9, F10 ~func(R) T10, F11 ~func(R) T11, F12 ~func(R) T12, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12) func(r []R) Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { + return func(r []R) Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { + return MakeTuple12( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + f8(r[7]), + f9(r[8]), + f10(r[9]), + f11(r[10]), + f12(r[11]), + ) + } +} + +// Push12 creates a [Tuple13] from a [Tuple12] by appending a constant value +func Push12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](value T13) func(Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { + return func(t Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { + return MakeTuple13(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, value) + } +} + +// MakeTuple13 is a function that converts its 13 parameters into a [Tuple13] +func MakeTuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { + return Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13} +} + +// Tupled13 converts a function with 13 parameters into a function taking a Tuple13 +// The inverse function is [Untupled13] +func Tupled13[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R any](f F) func(Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) R { + return func(t Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13) + } +} + +// Untupled13 converts a function with a [Tuple13] parameter into a function with 13 parameters +// The inverse function is [Tupled13] +func Untupled13[F ~func(Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) R { + return f(MakeTuple13(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13)) + } +} + +// Monoid13 creates a [Monoid] for a [Tuple13] based on 13 monoids for the contained types +func Monoid13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7], m8 M.Monoid[T8], m9 M.Monoid[T9], m10 M.Monoid[T10], m11 M.Monoid[T11], m12 M.Monoid[T12], m13 M.Monoid[T13]) M.Monoid[Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] { + return M.MakeMonoid(func(l, r Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { + return MakeTuple13(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7), m8.Concat(l.F8, r.F8), m9.Concat(l.F9, r.F9), m10.Concat(l.F10, r.F10), m11.Concat(l.F11, r.F11), m12.Concat(l.F12, r.F12), m13.Concat(l.F13, r.F13)) + }, MakeTuple13(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty(), m8.Empty(), m9.Empty(), m10.Empty(), m11.Empty(), m12.Empty(), m13.Empty())) +} + +// Ord13 creates n [Ord] for a [Tuple13] based on 13 [Ord]s for the contained types +func Ord13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9], o10 O.Ord[T10], o11 O.Ord[T11], o12 O.Ord[T12], o13 O.Ord[T13]) O.Ord[Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]] { + return O.MakeOrd(func(l, r Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + if c := o8.Compare(l.F8, r.F8); c != 0 { + return c + } + if c := o9.Compare(l.F9, r.F9); c != 0 { + return c + } + if c := o10.Compare(l.F10, r.F10); c != 0 { + return c + } + if c := o11.Compare(l.F11, r.F11); c != 0 { + return c + } + if c := o12.Compare(l.F12, r.F12); c != 0 { + return c + } + if c := o13.Compare(l.F13, r.F13); c != 0 { + return c + } + return 0 + }, func(l, r Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9) && o10.Equals(l.F10, r.F10) && o11.Equals(l.F11, r.F11) && o12.Equals(l.F12, r.F12) && o13.Equals(l.F13, r.F13) + }) +} + +// Map13 maps each value of a [Tuple13] via a mapping function +func Map13[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, F8 ~func(T8) R8, F9 ~func(T9) R9, F10 ~func(T10) R10, F11 ~func(T11) R11, F12 ~func(T12) R12, F13 ~func(T13) R13, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7, T8, R8, T9, R9, T10, R10, T11, R11, T12, R12, T13, R13 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) func(Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) Tuple13[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13] { + return func(t Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) Tuple13[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13] { + return MakeTuple13( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + f12(t.F12), + f13(t.F13), + ) + } +} + +// Replicate13 creates a [Tuple13] with all fields set to the input value `t` +func Replicate13[T any](t T) Tuple13[T, T, T, T, T, T, T, T, T, T, T, T, T] { + return MakeTuple13(t, t, t, t, t, t, t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple13] +func (t Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13) +} + +// MarshalJSON marshals the [Tuple13] into a JSON array +func (t Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple13] +func (t *Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7, &t.F8, &t.F9, &t.F10, &t.F11, &t.F12, &t.F13) +} + +// ToArray converts the [Tuple13] into an array of type [R] using 13 transformation functions from [T] to [R] +// The inverse function is [FromArray13] +func ToArray13[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, F8 ~func(T8) R, F9 ~func(T9) R, F10 ~func(T10) R, F11 ~func(T11) R, F12 ~func(T12) R, F13 ~func(T13) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) func(t Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) []R { + return func(t Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + f12(t.F12), + f13(t.F13), + } + } +} + +// FromArray converts an array of [R] into a [Tuple13] using 13 functions from [R] to [T] +// The inverse function is [ToArray13] +func FromArray13[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, F8 ~func(R) T8, F9 ~func(R) T9, F10 ~func(R) T10, F11 ~func(R) T11, F12 ~func(R) T12, F13 ~func(R) T13, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13) func(r []R) Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { + return func(r []R) Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { + return MakeTuple13( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + f8(r[7]), + f9(r[8]), + f10(r[9]), + f11(r[10]), + f12(r[11]), + f13(r[12]), + ) + } +} + +// Push13 creates a [Tuple14] from a [Tuple13] by appending a constant value +func Push13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](value T14) func(Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { + return func(t Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { + return MakeTuple14(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, value) + } +} + +// MakeTuple14 is a function that converts its 14 parameters into a [Tuple14] +func MakeTuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { + return Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14} +} + +// Tupled14 converts a function with 14 parameters into a function taking a Tuple14 +// The inverse function is [Untupled14] +func Tupled14[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R any](f F) func(Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) R { + return func(t Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, t.F14) + } +} + +// Untupled14 converts a function with a [Tuple14] parameter into a function with 14 parameters +// The inverse function is [Tupled14] +func Untupled14[F ~func(Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) R { + return f(MakeTuple14(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14)) + } +} + +// Monoid14 creates a [Monoid] for a [Tuple14] based on 14 monoids for the contained types +func Monoid14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7], m8 M.Monoid[T8], m9 M.Monoid[T9], m10 M.Monoid[T10], m11 M.Monoid[T11], m12 M.Monoid[T12], m13 M.Monoid[T13], m14 M.Monoid[T14]) M.Monoid[Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] { + return M.MakeMonoid(func(l, r Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { + return MakeTuple14(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7), m8.Concat(l.F8, r.F8), m9.Concat(l.F9, r.F9), m10.Concat(l.F10, r.F10), m11.Concat(l.F11, r.F11), m12.Concat(l.F12, r.F12), m13.Concat(l.F13, r.F13), m14.Concat(l.F14, r.F14)) + }, MakeTuple14(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty(), m8.Empty(), m9.Empty(), m10.Empty(), m11.Empty(), m12.Empty(), m13.Empty(), m14.Empty())) +} + +// Ord14 creates n [Ord] for a [Tuple14] based on 14 [Ord]s for the contained types +func Ord14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9], o10 O.Ord[T10], o11 O.Ord[T11], o12 O.Ord[T12], o13 O.Ord[T13], o14 O.Ord[T14]) O.Ord[Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]] { + return O.MakeOrd(func(l, r Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + if c := o8.Compare(l.F8, r.F8); c != 0 { + return c + } + if c := o9.Compare(l.F9, r.F9); c != 0 { + return c + } + if c := o10.Compare(l.F10, r.F10); c != 0 { + return c + } + if c := o11.Compare(l.F11, r.F11); c != 0 { + return c + } + if c := o12.Compare(l.F12, r.F12); c != 0 { + return c + } + if c := o13.Compare(l.F13, r.F13); c != 0 { + return c + } + if c := o14.Compare(l.F14, r.F14); c != 0 { + return c + } + return 0 + }, func(l, r Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9) && o10.Equals(l.F10, r.F10) && o11.Equals(l.F11, r.F11) && o12.Equals(l.F12, r.F12) && o13.Equals(l.F13, r.F13) && o14.Equals(l.F14, r.F14) + }) +} + +// Map14 maps each value of a [Tuple14] via a mapping function +func Map14[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, F8 ~func(T8) R8, F9 ~func(T9) R9, F10 ~func(T10) R10, F11 ~func(T11) R11, F12 ~func(T12) R12, F13 ~func(T13) R13, F14 ~func(T14) R14, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7, T8, R8, T9, R9, T10, R10, T11, R11, T12, R12, T13, R13, T14, R14 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) func(Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) Tuple14[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14] { + return func(t Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) Tuple14[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14] { + return MakeTuple14( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + f12(t.F12), + f13(t.F13), + f14(t.F14), + ) + } +} + +// Replicate14 creates a [Tuple14] with all fields set to the input value `t` +func Replicate14[T any](t T) Tuple14[T, T, T, T, T, T, T, T, T, T, T, T, T, T] { + return MakeTuple14(t, t, t, t, t, t, t, t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple14] +func (t Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, t.F14) +} + +// MarshalJSON marshals the [Tuple14] into a JSON array +func (t Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, t.F14) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple14] +func (t *Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7, &t.F8, &t.F9, &t.F10, &t.F11, &t.F12, &t.F13, &t.F14) +} + +// ToArray converts the [Tuple14] into an array of type [R] using 14 transformation functions from [T] to [R] +// The inverse function is [FromArray14] +func ToArray14[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, F8 ~func(T8) R, F9 ~func(T9) R, F10 ~func(T10) R, F11 ~func(T11) R, F12 ~func(T12) R, F13 ~func(T13) R, F14 ~func(T14) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) func(t Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) []R { + return func(t Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + f12(t.F12), + f13(t.F13), + f14(t.F14), + } + } +} + +// FromArray converts an array of [R] into a [Tuple14] using 14 functions from [R] to [T] +// The inverse function is [ToArray14] +func FromArray14[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, F8 ~func(R) T8, F9 ~func(R) T9, F10 ~func(R) T10, F11 ~func(R) T11, F12 ~func(R) T12, F13 ~func(R) T13, F14 ~func(R) T14, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14) func(r []R) Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { + return func(r []R) Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { + return MakeTuple14( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + f8(r[7]), + f9(r[8]), + f10(r[9]), + f11(r[10]), + f12(r[11]), + f13(r[12]), + f14(r[13]), + ) + } +} + +// Push14 creates a [Tuple15] from a [Tuple14] by appending a constant value +func Push14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](value T15) func(Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { + return func(t Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { + return MakeTuple15(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, t.F14, value) + } +} + +// MakeTuple15 is a function that converts its 15 parameters into a [Tuple15] +func MakeTuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15) Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { + return Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]{t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15} +} + +// Tupled15 converts a function with 15 parameters into a function taking a Tuple15 +// The inverse function is [Untupled15] +func Tupled15[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R any](f F) func(Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) R { + return func(t Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) R { + return f(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, t.F14, t.F15) + } +} + +// Untupled15 converts a function with a [Tuple15] parameter into a function with 15 parameters +// The inverse function is [Tupled15] +func Untupled15[F ~func(Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) R { + return func(t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15) R { + return f(MakeTuple15(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15)) + } +} + +// Monoid15 creates a [Monoid] for a [Tuple15] based on 15 monoids for the contained types +func Monoid15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](m1 M.Monoid[T1], m2 M.Monoid[T2], m3 M.Monoid[T3], m4 M.Monoid[T4], m5 M.Monoid[T5], m6 M.Monoid[T6], m7 M.Monoid[T7], m8 M.Monoid[T8], m9 M.Monoid[T9], m10 M.Monoid[T10], m11 M.Monoid[T11], m12 M.Monoid[T12], m13 M.Monoid[T13], m14 M.Monoid[T14], m15 M.Monoid[T15]) M.Monoid[Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] { + return M.MakeMonoid(func(l, r Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { + return MakeTuple15(m1.Concat(l.F1, r.F1), m2.Concat(l.F2, r.F2), m3.Concat(l.F3, r.F3), m4.Concat(l.F4, r.F4), m5.Concat(l.F5, r.F5), m6.Concat(l.F6, r.F6), m7.Concat(l.F7, r.F7), m8.Concat(l.F8, r.F8), m9.Concat(l.F9, r.F9), m10.Concat(l.F10, r.F10), m11.Concat(l.F11, r.F11), m12.Concat(l.F12, r.F12), m13.Concat(l.F13, r.F13), m14.Concat(l.F14, r.F14), m15.Concat(l.F15, r.F15)) + }, MakeTuple15(m1.Empty(), m2.Empty(), m3.Empty(), m4.Empty(), m5.Empty(), m6.Empty(), m7.Empty(), m8.Empty(), m9.Empty(), m10.Empty(), m11.Empty(), m12.Empty(), m13.Empty(), m14.Empty(), m15.Empty())) +} + +// Ord15 creates n [Ord] for a [Tuple15] based on 15 [Ord]s for the contained types +func Ord15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](o1 O.Ord[T1], o2 O.Ord[T2], o3 O.Ord[T3], o4 O.Ord[T4], o5 O.Ord[T5], o6 O.Ord[T6], o7 O.Ord[T7], o8 O.Ord[T8], o9 O.Ord[T9], o10 O.Ord[T10], o11 O.Ord[T11], o12 O.Ord[T12], o13 O.Ord[T13], o14 O.Ord[T14], o15 O.Ord[T15]) O.Ord[Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]] { + return O.MakeOrd(func(l, r Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) int { + if c := o1.Compare(l.F1, r.F1); c != 0 { + return c + } + if c := o2.Compare(l.F2, r.F2); c != 0 { + return c + } + if c := o3.Compare(l.F3, r.F3); c != 0 { + return c + } + if c := o4.Compare(l.F4, r.F4); c != 0 { + return c + } + if c := o5.Compare(l.F5, r.F5); c != 0 { + return c + } + if c := o6.Compare(l.F6, r.F6); c != 0 { + return c + } + if c := o7.Compare(l.F7, r.F7); c != 0 { + return c + } + if c := o8.Compare(l.F8, r.F8); c != 0 { + return c + } + if c := o9.Compare(l.F9, r.F9); c != 0 { + return c + } + if c := o10.Compare(l.F10, r.F10); c != 0 { + return c + } + if c := o11.Compare(l.F11, r.F11); c != 0 { + return c + } + if c := o12.Compare(l.F12, r.F12); c != 0 { + return c + } + if c := o13.Compare(l.F13, r.F13); c != 0 { + return c + } + if c := o14.Compare(l.F14, r.F14); c != 0 { + return c + } + if c := o15.Compare(l.F15, r.F15); c != 0 { + return c + } + return 0 + }, func(l, r Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) bool { + return o1.Equals(l.F1, r.F1) && o2.Equals(l.F2, r.F2) && o3.Equals(l.F3, r.F3) && o4.Equals(l.F4, r.F4) && o5.Equals(l.F5, r.F5) && o6.Equals(l.F6, r.F6) && o7.Equals(l.F7, r.F7) && o8.Equals(l.F8, r.F8) && o9.Equals(l.F9, r.F9) && o10.Equals(l.F10, r.F10) && o11.Equals(l.F11, r.F11) && o12.Equals(l.F12, r.F12) && o13.Equals(l.F13, r.F13) && o14.Equals(l.F14, r.F14) && o15.Equals(l.F15, r.F15) + }) +} + +// Map15 maps each value of a [Tuple15] via a mapping function +func Map15[F1 ~func(T1) R1, F2 ~func(T2) R2, F3 ~func(T3) R3, F4 ~func(T4) R4, F5 ~func(T5) R5, F6 ~func(T6) R6, F7 ~func(T7) R7, F8 ~func(T8) R8, F9 ~func(T9) R9, F10 ~func(T10) R10, F11 ~func(T11) R11, F12 ~func(T12) R12, F13 ~func(T13) R13, F14 ~func(T14) R14, F15 ~func(T15) R15, T1, R1, T2, R2, T3, R3, T4, R4, T5, R5, T6, R6, T7, R7, T8, R8, T9, R9, T10, R10, T11, R11, T12, R12, T13, R13, T14, R14, T15, R15 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) func(Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) Tuple15[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15] { + return func(t Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) Tuple15[R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15] { + return MakeTuple15( + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + f12(t.F12), + f13(t.F13), + f14(t.F14), + f15(t.F15), + ) + } +} + +// Replicate15 creates a [Tuple15] with all fields set to the input value `t` +func Replicate15[T any](t T) Tuple15[T, T, T, T, T, T, T, T, T, T, T, T, T, T, T] { + return MakeTuple15(t, t, t, t, t, t, t, t, t, t, t, t, t, t, t) +} + +// String prints some debug info for the [Tuple15] +func (t Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) String() string { + return tupleString(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, t.F14, t.F15) +} + +// MarshalJSON marshals the [Tuple15] into a JSON array +func (t Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) MarshalJSON() ([]byte, error) { + return tupleMarshalJSON(t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, t.F14, t.F15) +} + +// UnmarshalJSON unmarshals a JSON array into a [Tuple15] +func (t *Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) UnmarshalJSON(data []byte) error { + return tupleUnmarshalJSON(data, &t.F1, &t.F2, &t.F3, &t.F4, &t.F5, &t.F6, &t.F7, &t.F8, &t.F9, &t.F10, &t.F11, &t.F12, &t.F13, &t.F14, &t.F15) +} + +// ToArray converts the [Tuple15] into an array of type [R] using 15 transformation functions from [T] to [R] +// The inverse function is [FromArray15] +func ToArray15[F1 ~func(T1) R, F2 ~func(T2) R, F3 ~func(T3) R, F4 ~func(T4) R, F5 ~func(T5) R, F6 ~func(T6) R, F7 ~func(T7) R, F8 ~func(T8) R, F9 ~func(T9) R, F10 ~func(T10) R, F11 ~func(T11) R, F12 ~func(T12) R, F13 ~func(T13) R, F14 ~func(T14) R, F15 ~func(T15) R, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) func(t Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) []R { + return func(t Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) []R { + return []R{ + f1(t.F1), + f2(t.F2), + f3(t.F3), + f4(t.F4), + f5(t.F5), + f6(t.F6), + f7(t.F7), + f8(t.F8), + f9(t.F9), + f10(t.F10), + f11(t.F11), + f12(t.F12), + f13(t.F13), + f14(t.F14), + f15(t.F15), + } + } +} + +// FromArray converts an array of [R] into a [Tuple15] using 15 functions from [R] to [T] +// The inverse function is [ToArray15] +func FromArray15[F1 ~func(R) T1, F2 ~func(R) T2, F3 ~func(R) T3, F4 ~func(R) T4, F5 ~func(R) T5, F6 ~func(R) T6, F7 ~func(R) T7, F8 ~func(R) T8, F9 ~func(R) T9, F10 ~func(R) T10, F11 ~func(R) T11, F12 ~func(R) T12, F13 ~func(R) T13, F14 ~func(R) T14, F15 ~func(R) T15, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10, f11 F11, f12 F12, f13 F13, f14 F14, f15 F15) func(r []R) Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { + return func(r []R) Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { + return MakeTuple15( + f1(r[0]), + f2(r[1]), + f3(r[2]), + f4(r[3]), + f5(r[4]), + f6(r[5]), + f7(r[6]), + f8(r[7]), + f9(r[8]), + f10(r[9]), + f11(r[10]), + f12(r[11]), + f13(r[12]), + f14(r[13]), + f15(r[14]), + ) + } +} diff --git a/v2/tuple/tuple.go b/v2/tuple/tuple.go new file mode 100644 index 0000000..626cac4 --- /dev/null +++ b/v2/tuple/tuple.go @@ -0,0 +1,84 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package tuple contains type definitions and functions for data structures for tuples of heterogenous types. For homogeneous types +// consider to use arrays for simplicity +package tuple + +import ( + "encoding/json" + "fmt" + "strings" + + N "github.com/IBM/fp-go/v2/number" +) + +func Of[T1 any](t T1) Tuple1[T1] { + return MakeTuple1(t) +} + +// First returns the first element of a [Tuple2] +func First[T1, T2 any](t Tuple2[T1, T2]) T1 { + return t.F1 +} + +// Second returns the second element of a [Tuple2] +func Second[T1, T2 any](t Tuple2[T1, T2]) T2 { + return t.F2 +} + +func Swap[T1, T2 any](t Tuple2[T1, T2]) Tuple2[T2, T1] { + return MakeTuple2(t.F2, t.F1) +} + +func Of2[T1, T2 any](e T2) func(T1) Tuple2[T1, T2] { + return func(t T1) Tuple2[T1, T2] { + return MakeTuple2(t, e) + } +} + +func BiMap[E, G, A, B any](mapSnd func(E) G, mapFst func(A) B) func(Tuple2[A, E]) Tuple2[B, G] { + return func(t Tuple2[A, E]) Tuple2[B, G] { + return MakeTuple2(mapFst(First(t)), mapSnd(Second(t))) + } +} + +// marshalJSON marshals the tuple into a JSON array +func tupleMarshalJSON(src ...any) ([]byte, error) { + return json.Marshal(src) +} + +// tupleUnmarshalJSON unmarshals a JSON array into a tuple +func tupleUnmarshalJSON(data []byte, dst ...any) error { + var src []json.RawMessage + if err := json.Unmarshal(data, &src); err != nil { + return err + } + l := N.Min(len(src), len(dst)) + // unmarshal + for i := 0; i < l; i++ { + if err := json.Unmarshal(src[i], dst[i]); err != nil { + return err + } + } + // successfully decoded the tuple + return nil +} + +// tupleString converts a tuple to a string +func tupleString(src ...any) string { + l := len(src) + return fmt.Sprintf("Tuple%d[%s](%s)", l, fmt.Sprintf(strings.Repeat(", %T", l)[2:], src...), fmt.Sprintf(strings.Repeat(", %v", l)[2:], src...)) +} diff --git a/v2/tuple/tuple_test.go b/v2/tuple/tuple_test.go new file mode 100644 index 0000000..51018a2 --- /dev/null +++ b/v2/tuple/tuple_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tuple + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestString(t *testing.T) { + + value := MakeTuple2("Carsten", 1) + + assert.Equal(t, "Tuple2[string, int](Carsten, 1)", value.String()) + +} + +func TestMarshal(t *testing.T) { + + value := MakeTuple3("Carsten", 1, true) + + data, err := json.Marshal(value) + require.NoError(t, err) + + var unmarshaled Tuple3[string, int, bool] + err = json.Unmarshal(data, &unmarshaled) + require.NoError(t, err) + + assert.Equal(t, value, unmarshaled) +} + +func TestMarshalSmallArray(t *testing.T) { + + value := `["Carsten"]` + + var unmarshaled Tuple3[string, int, bool] + err := json.Unmarshal([]byte(value), &unmarshaled) + require.NoError(t, err) + + assert.Equal(t, MakeTuple3("Carsten", 0, false), unmarshaled) +}