From d0e4984b60d119d85bb67278f9e4ea165dcc2399 Mon Sep 17 00:00:00 2001 From: "Dr. Carsten Leue" Date: Mon, 12 Feb 2024 10:33:42 +0100 Subject: [PATCH] fix: switch internal implementation of iterator from Tuple2 to Pair Signed-off-by: Dr. Carsten Leue --- context/readerioeither/http/request.go | 4 +- exec/exec.go | 8 +- http/types.go | 8 +- http/utils.go | 14 ++-- internal/exec/exec.go | 4 +- ioeither/http/request.go | 4 +- iterator/stateless/compress.go | 4 +- iterator/stateless/generic/any.go | 4 +- iterator/stateless/generic/bind.go | 14 ++-- iterator/stateless/generic/compress.go | 8 +- iterator/stateless/generic/cycle.go | 12 +-- iterator/stateless/generic/dropwhile.go | 14 ++-- iterator/stateless/generic/first.go | 6 +- iterator/stateless/generic/io.go | 8 +- iterator/stateless/generic/iterator.go | 106 ++++++++++++------------ iterator/stateless/generic/last.go | 4 +- iterator/stateless/generic/monad.go | 6 +- iterator/stateless/generic/monoid.go | 4 +- iterator/stateless/generic/reflect.go | 8 +- iterator/stateless/generic/scan.go | 10 +-- iterator/stateless/generic/take.go | 6 +- iterator/stateless/generic/uniq.go | 14 ++-- iterator/stateless/generic/zip.go | 20 ++--- iterator/stateless/iterator.go | 12 +-- iterator/stateless/scan_test.go | 14 ++-- iterator/stateless/zip.go | 6 +- iterator/stateless/zip_test.go | 4 +- option/pair.go | 30 +++++++ pair/generic/sequence.go | 71 ++++++++++++++++ pair/pair.go | 49 ++++++++--- 30 files changed, 301 insertions(+), 175 deletions(-) create mode 100644 option/pair.go create mode 100644 pair/generic/sequence.go diff --git a/context/readerioeither/http/request.go b/context/readerioeither/http/request.go index 5699ba5..a0b4eda 100644 --- a/context/readerioeither/http/request.go +++ b/context/readerioeither/http/request.go @@ -26,7 +26,7 @@ import ( IOE "github.com/IBM/fp-go/ioeither" IOEF "github.com/IBM/fp-go/ioeither/file" J "github.com/IBM/fp-go/json" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) type ( @@ -79,7 +79,7 @@ func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullR IOE.Of[error, io.ReadCloser], IOEF.ReadAll[io.ReadCloser], ), - IOE.Map[error](F.Bind1st(T.MakeTuple2[*http.Response, []byte], resp)), + IOE.Map[error](F.Bind1st(P.MakePair[*http.Response, []byte], resp)), ) }), ) diff --git a/exec/exec.go b/exec/exec.go index ba4b4ae..2597238 100644 --- a/exec/exec.go +++ b/exec/exec.go @@ -16,18 +16,18 @@ package exec import ( - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/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 = T.Tuple2[[]byte, []byte] + CommandOutput = P.Pair[[]byte, []byte] ) var ( // StdOut returns the field of a [CommandOutput] representing `stdout` - StdOut = T.First[[]byte, []byte] + StdOut = P.Head[[]byte, []byte] // StdErr returns the field of a [CommandOutput] representing `stderr` - StdErr = T.Second[[]byte, []byte] + StdErr = P.Tail[[]byte, []byte] ) diff --git a/http/types.go b/http/types.go index f5432cf..3c3cb61 100644 --- a/http/types.go +++ b/http/types.go @@ -18,15 +18,15 @@ package http import ( H "net/http" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) type ( // FullResponse represents a full http response, including headers and body - FullResponse = T.Tuple2[*H.Response, []byte] + FullResponse = P.Pair[*H.Response, []byte] ) var ( - Response = T.First[*H.Response, []byte] - Body = T.Second[*H.Response, []byte] + Response = P.Head[*H.Response, []byte] + Body = P.Tail[*H.Response, []byte] ) diff --git a/http/utils.go b/http/utils.go index 9e2c9c6..1137685 100644 --- a/http/utils.go +++ b/http/utils.go @@ -28,12 +28,12 @@ import ( "github.com/IBM/fp-go/errors" F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" + P "github.com/IBM/fp-go/pair" R "github.com/IBM/fp-go/record/generic" - T "github.com/IBM/fp-go/tuple" ) type ( - ParsedMediaType = T.Tuple2[string, map[string]string] + ParsedMediaType = P.Pair[string, map[string]string] HttpError struct { statusCode int @@ -45,17 +45,15 @@ type ( var ( // mime type to check if a media type matches - reJSONMimeType = regexp.MustCompile(`application/(?:\w+\+)?json`) + 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( - T.First[string, map[string]string], - E.FromPredicate(reJSONMimeType.MatchString, func(mimeType string) error { - return fmt.Errorf("mimetype [%s] is not a valid JSON content type", mimeType) - }), + 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 @@ -81,7 +79,7 @@ const ( // 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(T.MakeTuple2(m, p), err) + return E.TryCatchError(P.MakePair(m, p), err) } // Error fulfills the error interface diff --git a/internal/exec/exec.go b/internal/exec/exec.go index d579152..e1a68e4 100644 --- a/internal/exec/exec.go +++ b/internal/exec/exec.go @@ -23,7 +23,7 @@ import ( EX "github.com/IBM/fp-go/exec" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) func Exec(ctx context.Context, name string, args []string, in []byte) (EX.CommandOutput, error) { @@ -42,5 +42,5 @@ func Exec(ctx context.Context, name string, args []string, in []byte) (EX.Comman 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 T.MakeTuple2(stdOut.Bytes(), stdErr.Bytes()), err + return P.MakePair(stdOut.Bytes(), stdErr.Bytes()), err } diff --git a/ioeither/http/request.go b/ioeither/http/request.go index 07d079e..2dd9ee4 100644 --- a/ioeither/http/request.go +++ b/ioeither/http/request.go @@ -27,7 +27,7 @@ import ( IOE "github.com/IBM/fp-go/ioeither" IOEF "github.com/IBM/fp-go/ioeither/file" J "github.com/IBM/fp-go/json" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) type ( @@ -95,7 +95,7 @@ func ReadFullResponse(client Client) func(Requester) IOE.IOEither[error, H.FullR IOE.Of[error, io.ReadCloser], IOEF.ReadAll[io.ReadCloser], ), - IOE.Map[error](F.Bind1st(T.MakeTuple2[*http.Response, []byte], resp)), + IOE.Map[error](F.Bind1st(P.MakePair[*http.Response, []byte], resp)), ) }), ) diff --git a/iterator/stateless/compress.go b/iterator/stateless/compress.go index 3506629..4bd87be 100644 --- a/iterator/stateless/compress.go +++ b/iterator/stateless/compress.go @@ -17,11 +17,11 @@ package stateless import ( G "github.com/IBM/fp-go/iterator/stateless/generic" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/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[T.Tuple2[U, bool]]](sel) + return G.Compress[Iterator[U], Iterator[bool], Iterator[P.Pair[U, bool]]](sel) } diff --git a/iterator/stateless/generic/any.go b/iterator/stateless/generic/any.go index 885bc4d..ebd0b92 100644 --- a/iterator/stateless/generic/any.go +++ b/iterator/stateless/generic/any.go @@ -18,11 +18,11 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/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[T.Tuple2[GU, U]], FCT ~func(U) bool, U any](pred FCT) func(ma GU) bool { +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], diff --git a/iterator/stateless/generic/bind.go b/iterator/stateless/generic/bind.go index a7fd9cf..5401637 100644 --- a/iterator/stateless/generic/bind.go +++ b/iterator/stateless/generic/bind.go @@ -20,18 +20,18 @@ import ( C "github.com/IBM/fp-go/internal/chain" F "github.com/IBM/fp-go/internal/functor" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) // Bind creates an empty context of type [S] to be used with the [Bind] operation -func Do[GS ~func() O.Option[T.Tuple2[GS, S]], S any]( +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[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, S2, A any]( +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 { @@ -45,7 +45,7 @@ func Bind[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2 } // Let attaches the result of a computation to a context [S1] to produce a context [S2] -func Let[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], S1, S2, A any]( +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 { @@ -57,7 +57,7 @@ func Let[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[ } // LetTo attaches the a value to a context [S1] to produce a context [S2] -func LetTo[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], S1, S2, B any]( +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 { @@ -69,7 +69,7 @@ func LetTo[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple } // BindTo initializes a new state [S1] from a value [T] -func BindTo[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, A any]( +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( @@ -79,7 +79,7 @@ func BindTo[GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GA ~func() O.Option[T.Tuple } // 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[T.Tuple2[GAS2, func(A) S2]], GS1 ~func() O.Option[T.Tuple2[GS1, S1]], GS2 ~func() O.Option[T.Tuple2[GS2, S2]], GA ~func() O.Option[T.Tuple2[GA, A]], S1, S2, A any]( +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 { diff --git a/iterator/stateless/generic/compress.go b/iterator/stateless/generic/compress.go index a3642bc..1e2ec38 100644 --- a/iterator/stateless/generic/compress.go +++ b/iterator/stateless/generic/compress.go @@ -18,17 +18,17 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/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[T.Tuple2[GU, U]], GB ~func() O.Option[T.Tuple2[GB, bool]], CS ~func() O.Option[T.Tuple2[CS, T.Tuple2[U, bool]]], U any](sel GB) func(GU) GU { +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(T.Second[U, bool]), - O.Map(T.First[U, bool]), + O.FromPredicate(P.Tail[U, bool]), + O.Map(P.Head[U, bool]), )), ) } diff --git a/iterator/stateless/generic/cycle.go b/iterator/stateless/generic/cycle.go index 31ce5b0..fc009f8 100644 --- a/iterator/stateless/generic/cycle.go +++ b/iterator/stateless/generic/cycle.go @@ -18,12 +18,12 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) -func Cycle[GU ~func() O.Option[T.Tuple2[GU, U]], U any](ma GU) GU { +func Cycle[GU ~func() O.Option[P.Pair[GU, U]], U any](ma GU) GU { // avoid cyclic references - var m func(O.Option[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GU, U]] + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GU, U]] recurse := func(mu GU) GU { return F.Nullary2( @@ -32,11 +32,11 @@ func Cycle[GU ~func() O.Option[T.Tuple2[GU, U]], U any](ma GU) GU { ) } - m = O.Fold(func() O.Option[T.Tuple2[GU, U]] { + m = O.Fold(func() O.Option[P.Pair[GU, U]] { return recurse(ma)() }, F.Flow2( - T.Map2(recurse, F.Identity[U]), - O.Of[T.Tuple2[GU, U]], + P.BiMap(recurse, F.Identity[U]), + O.Of[P.Pair[GU, U]], )) return recurse(ma) diff --git a/iterator/stateless/generic/dropwhile.go b/iterator/stateless/generic/dropwhile.go index 5464894..10bbd0d 100644 --- a/iterator/stateless/generic/dropwhile.go +++ b/iterator/stateless/generic/dropwhile.go @@ -18,17 +18,17 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - P "github.com/IBM/fp-go/predicate" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" + PR "github.com/IBM/fp-go/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[T.Tuple2[GU, U]], U any](pred func(U) bool) func(GU) GU { +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[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GU, U]] + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GU, U]] - fromPred := O.FromPredicate(P.Not(P.ContraMap(T.Second[GU, U])(pred))) + fromPred := O.FromPredicate(PR.Not(PR.ContraMap(P.Tail[GU, U])(pred))) recurse := func(mu GU) GU { return F.Nullary2( @@ -37,11 +37,11 @@ func DropWhile[GU ~func() O.Option[T.Tuple2[GU, U]], U any](pred func(U) bool) f ) } - m = O.Chain(func(t T.Tuple2[GU, U]) O.Option[T.Tuple2[GU, U]] { + 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[T.Tuple2[GU, U]]), + O.Fold(recurse(Next(t)), O.Of[P.Pair[GU, U]]), ) }) diff --git a/iterator/stateless/generic/first.go b/iterator/stateless/generic/first.go index 8eff4fe..8febce6 100644 --- a/iterator/stateless/generic/first.go +++ b/iterator/stateless/generic/first.go @@ -18,13 +18,13 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) // First returns the first item in an iterator if such an item exists -func First[GU ~func() O.Option[T.Tuple2[GU, U]], U any](mu GU) O.Option[U] { +func First[GU ~func() O.Option[P.Pair[GU, U]], U any](mu GU) O.Option[U] { return F.Pipe1( mu(), - O.Map(T.Second[GU, U]), + O.Map(P.Tail[GU, U]), ) } diff --git a/iterator/stateless/generic/io.go b/iterator/stateless/generic/io.go index 7287884..30b538d 100644 --- a/iterator/stateless/generic/io.go +++ b/iterator/stateless/generic/io.go @@ -19,16 +19,16 @@ import ( F "github.com/IBM/fp-go/function" L "github.com/IBM/fp-go/io/generic" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) // FromLazy returns an iterator on top of a lazy function -func FromLazy[GU ~func() O.Option[T.Tuple2[GU, U]], LZ ~func() U, U any](l LZ) GU { +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(T.MakeTuple2[GU, U], Empty[GU]()), - O.Of[T.Tuple2[GU, U]], + F.Bind1st(P.MakePair[GU, U], Empty[GU]()), + O.Of[P.Pair[GU, U]], )), ) } diff --git a/iterator/stateless/generic/iterator.go b/iterator/stateless/generic/iterator.go index ff91465..ee0b5bc 100644 --- a/iterator/stateless/generic/iterator.go +++ b/iterator/stateless/generic/iterator.go @@ -24,45 +24,45 @@ import ( M "github.com/IBM/fp-go/monoid" N "github.com/IBM/fp-go/number" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) -// Next returns the iterator for the next element in an iterator `T.Tuple2` -func Next[GU ~func() O.Option[T.Tuple2[GU, U]], U any](m T.Tuple2[GU, U]) GU { - return T.First(m) +// 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 `T.Tuple2` -func Current[GU ~func() O.Option[T.Tuple2[GU, U]], U any](m T.Tuple2[GU, U]) U { - return T.Second(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[T.Tuple2[GU, U]], U any](data ...U) GU { +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[T.Tuple2[GU, U]], U any]() GU { +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[T.Tuple2[GU, U]], U any](a U) GU { - return IO.Of[GU](T.MakeTuple2(Empty[GU](), a)) +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[T.Tuple2[GU, U]], US ~[]U, U any](as US) GU { +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[T.Tuple2[GU, U]] { - return O.Of(T.MakeTuple2(FromArray[GU](tail), head)) + 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[T.Tuple2[GU, U]], U, V any](as GU, f func(V, U) V, initial V) V { +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 { @@ -74,18 +74,18 @@ func reduce[GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](as GU, f func(V, U) } // Reduce applies a function for each value of the iterator with a floating result -func Reduce[GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](f func(V, U) V, initial V) func(GU) V { +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[T.Tuple2[GU, U]], US ~[]U, U any](u GU) US { +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[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) V, U, V any](f FCT) func(ma GU) GV { +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[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GV, V]] + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] recurse := func(ma GU) GV { return F.Nullary2( @@ -94,17 +94,17 @@ func Map[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, ) } - m = O.Map(T.Map2(recurse, f)) + m = O.Map(P.BiMap(recurse, f)) return recurse } -func MonadMap[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](ma GU, f func(U) V) GV { +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[T.Tuple2[GU, U]], U any](right, left GU) GU { - var m func(ma O.Option[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GU, U]] +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) @@ -113,16 +113,16 @@ func concat[GU ~func() O.Option[T.Tuple2[GU, U]], U any](right, left GU) GU { m = O.Fold( right, F.Flow2( - T.Map2(recurse, F.Identity[U]), - O.Some[T.Tuple2[GU, U]], + P.BiMap(recurse, F.Identity[U]), + O.Some[P.Pair[GU, U]], )) return recurse(left) } -func Chain[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](f func(U) GV) func(GU) GV { +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[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GV, V]] + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] recurse := func(ma GU) GV { return F.Nullary2( @@ -132,9 +132,9 @@ func Chain[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU } m = O.Chain( F.Flow3( - T.Map2(recurse, f), - T.Tupled2(concat[GV]), - func(v GV) O.Option[T.Tuple2[GV, V]] { + P.BiMap(recurse, f), + P.Paired(concat[GV]), + func(v GV) O.Option[P.Pair[GV, V]] { return v() }, ), @@ -143,11 +143,11 @@ func Chain[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU return recurse } -func MonadChain[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](ma GU, f func(U) GV) GV { +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[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](ma GU, f func(U) GV) GU { +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], @@ -156,7 +156,7 @@ func MonadChainFirst[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T ) } -func ChainFirst[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](f func(U) GV) func(GU) GU { +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], @@ -164,14 +164,14 @@ func ChainFirst[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tupl ) } -func Flatten[GV ~func() O.Option[T.Tuple2[GV, GU]], GU ~func() O.Option[T.Tuple2[GU, U]], U any](ma GV) GU { +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[T.Tuple2[GU, U]], FCT ~func(int) U, U any](f FCT) GU { +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[T.Tuple2[GU, U]] + var m func(int) O.Option[P.Pair[GU, U]] recurse := func(i int) GU { return F.Nullary2( @@ -181,12 +181,12 @@ func MakeBy[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(int) U, U any](f FCT } m = F.Flow3( - T.Replicate2[int], - T.Map2(F.Flow2( + P.Of[int], + P.BiMap(F.Flow2( utils.Inc, recurse), f), - O.Of[T.Tuple2[GU, U]], + O.Of[P.Pair[GU, U]], ) // bootstrap @@ -194,13 +194,13 @@ func MakeBy[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(int) U, U any](f FCT } // Replicate creates an infinite [Iterator] containing a value. -func Replicate[GU ~func() O.Option[T.Tuple2[GU, U]], U any](a U) GU { +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[T.Tuple2[GU, U]], U any](n int, a U) GU { +func Repeat[GU ~func() O.Option[P.Pair[GU, U]], U any](n int, a U) GU { return F.Pipe2( a, Replicate[GU], @@ -209,13 +209,13 @@ func Repeat[GU ~func() O.Option[T.Tuple2[GU, U]], U any](n int, a U) GU { } // Count creates an [Iterator] containing a consecutive sequence of integers starting with the provided start value -func Count[GU ~func() O.Option[T.Tuple2[GU, int]]](start int) GU { +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[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) O.Option[V], U, V any](f FCT) func(ma GU) GV { +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[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GV, V]] + var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] recurse := func(ma GU) GV { return F.Nullary2( @@ -226,11 +226,11 @@ func FilterMap[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple m = O.Fold( Empty[GV](), - func(t T.Tuple2[GU, U]) O.Option[T.Tuple2[GV, V]] { + 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(T.MakeTuple2[GV, V], r), - O.Some[T.Tuple2[GV, V]], + F.Bind1st(P.MakePair[GV, V], r), + O.Some[P.Pair[GV, V]], )) }, ) @@ -238,26 +238,26 @@ func FilterMap[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple return recurse } -func Filter[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) bool, U any](f FCT) func(ma GU) GU { +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[T.Tuple2[GUV, func(U) V]], GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](ma GU) func(fab GUV) GV { +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[T.Tuple2[GUV, func(U) V]], GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](fab GUV, ma GU) GV { +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[T.Tuple2[GVV, GV]], GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) O.Option[GV], U, V any](f FCT) func(ma GU) GV { +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[T.Tuple2[GU, U]], FCT ~func(U) V, U, V any](m M.Monoid[V]) func(FCT) func(ma GU) V { +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)) @@ -265,6 +265,6 @@ func FoldMap[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) V, U, V any](m M } } -func Fold[GU ~func() O.Option[T.Tuple2[GU, U]], U any](m M.Monoid[U]) func(ma GU) U { +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/iterator/stateless/generic/last.go b/iterator/stateless/generic/last.go index 6f6f61f..7111892 100644 --- a/iterator/stateless/generic/last.go +++ b/iterator/stateless/generic/last.go @@ -18,10 +18,10 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) // Last returns the last item in an iterator if such an item exists -func Last[GU ~func() O.Option[T.Tuple2[GU, U]], U any](mu GU) O.Option[U] { +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/iterator/stateless/generic/monad.go b/iterator/stateless/generic/monad.go index 6877310..0355356 100644 --- a/iterator/stateless/generic/monad.go +++ b/iterator/stateless/generic/monad.go @@ -18,10 +18,10 @@ package generic import ( "github.com/IBM/fp-go/internal/monad" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) -type iteratorMonad[A, B any, GA ~func() O.Option[T.Tuple2[GA, A]], GB ~func() O.Option[T.Tuple2[GB, B]], GAB ~func() O.Option[T.Tuple2[GAB, func(A) B]]] struct{} +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) @@ -40,6 +40,6 @@ func (o *iteratorMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB { } // Monad implements the monadic operations for iterators -func Monad[A, B any, GA ~func() O.Option[T.Tuple2[GA, A]], GB ~func() O.Option[T.Tuple2[GB, B]], GAB ~func() O.Option[T.Tuple2[GAB, func(A) B]]]() monad.Monad[A, B, GA, GB, GAB] { +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/iterator/stateless/generic/monoid.go b/iterator/stateless/generic/monoid.go index da68831..a1ae7ed 100644 --- a/iterator/stateless/generic/monoid.go +++ b/iterator/stateless/generic/monoid.go @@ -19,10 +19,10 @@ import ( F "github.com/IBM/fp-go/function" M "github.com/IBM/fp-go/monoid" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) -func Monoid[GU ~func() O.Option[T.Tuple2[GU, U]], U any]() M.Monoid[GU] { +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/iterator/stateless/generic/reflect.go b/iterator/stateless/generic/reflect.go index 9ddc9d9..f7dfb3a 100644 --- a/iterator/stateless/generic/reflect.go +++ b/iterator/stateless/generic/reflect.go @@ -24,10 +24,10 @@ import ( N "github.com/IBM/fp-go/number" I "github.com/IBM/fp-go/number/integer" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) -func FromReflect[GR ~func() O.Option[T.Tuple2[GR, R.Value]]](val R.Value) GR { +func FromReflect[GR ~func() O.Option[P.Pair[GR, R.Value]]](val R.Value) GR { // recursive callback var recurse func(idx int) GR @@ -41,8 +41,8 @@ func FromReflect[GR ~func() O.Option[T.Tuple2[GR, R.Value]]](val R.Value) GR { L.Map(fromPred), LG.Map[L.Lazy[O.Option[int]], GR](O.Map( F.Flow2( - T.Replicate2[int], - T.Map2(F.Flow2(N.Add(1), recurse), val.Index), + P.Of[int], + P.BiMap(F.Flow2(N.Add(1), recurse), val.Index), ), )), ) diff --git a/iterator/stateless/generic/scan.go b/iterator/stateless/generic/scan.go index 58385f0..e88ece5 100644 --- a/iterator/stateless/generic/scan.go +++ b/iterator/stateless/generic/scan.go @@ -18,14 +18,14 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) -func apTuple[A, B any](t T.Tuple2[func(A) B, A]) T.Tuple2[B, A] { - return T.MakeTuple2(t.F1(t.F2), t.F2) +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[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(V, U) V, U, V any](f FCT, initial V) func(ma GU) GV { +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 @@ -33,7 +33,7 @@ func Scan[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, return F.Nullary2( ma, O.Map(F.Flow2( - T.Map2(m, F.Bind1st(f, current)), + P.BiMap(m, F.Bind1st(f, current)), apTuple[V, GV], )), ) diff --git a/iterator/stateless/generic/take.go b/iterator/stateless/generic/take.go index 166dc33..3dc8002 100644 --- a/iterator/stateless/generic/take.go +++ b/iterator/stateless/generic/take.go @@ -19,10 +19,10 @@ import ( F "github.com/IBM/fp-go/function" N "github.com/IBM/fp-go/number/integer" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) -func Take[GU ~func() O.Option[T.Tuple2[GU, U]], U any](n int) func(ma GU) GU { +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 @@ -34,7 +34,7 @@ func Take[GU ~func() O.Option[T.Tuple2[GU, U]], U any](n int) func(ma GU) GU { fromPred, O.Chain(F.Ignore1of1[int](F.Nullary2( ma, - O.Map(T.Map2(F.Bind2nd(recurse, idx+1), F.Identity[U])), + O.Map(P.BiMap(F.Bind2nd(recurse, idx+1), F.Identity[U])), ))), ) } diff --git a/iterator/stateless/generic/uniq.go b/iterator/stateless/generic/uniq.go index 315d677..910ffec 100644 --- a/iterator/stateless/generic/uniq.go +++ b/iterator/stateless/generic/uniq.go @@ -18,7 +18,7 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) // addToMap makes a deep copy of a map and adds a value @@ -31,23 +31,23 @@ func addToMap[A comparable](a A, m map[A]bool) map[A]bool { return cpy } -func Uniq[AS ~func() O.Option[T.Tuple2[AS, A]], K comparable, A any](f func(A) K) func(as AS) AS { +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 T.Tuple2[AS, A]) O.Option[T.Tuple2[AS, A]] { + O.Chain(func(a P.Pair[AS, A]) O.Option[P.Pair[AS, A]] { return F.Pipe3( - a.F2, + P.Tail(a), f, O.FromPredicate(func(k K) bool { _, ok := mp[k] return !ok }), - O.Fold(recurse(a.F1, mp), func(k K) O.Option[T.Tuple2[AS, A]] { - return O.Of(T.MakeTuple2(recurse(a.F1, addToMap(k, mp)), a.F2)) + 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))) }), ) }), @@ -57,6 +57,6 @@ func Uniq[AS ~func() O.Option[T.Tuple2[AS, A]], K comparable, A any](f func(A) K return F.Bind2nd(recurse, make(map[K]bool, 0)) } -func StrictUniq[AS ~func() O.Option[T.Tuple2[AS, A]], A comparable](as AS) AS { +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/iterator/stateless/generic/zip.go b/iterator/stateless/generic/zip.go index cd8bfe0..a75ed7a 100644 --- a/iterator/stateless/generic/zip.go +++ b/iterator/stateless/generic/zip.go @@ -18,29 +18,29 @@ package generic import ( F "github.com/IBM/fp-go/function" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/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[T.Tuple2[AS, A]], BS ~func() O.Option[T.Tuple2[BS, B]], CS ~func() O.Option[T.Tuple2[CS, C]], FCT ~func(A, B) C, A, B, C any](fa AS, fb BS, f FCT) CS { +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(T.Tuple2[O.Option[T.Tuple2[AS, A]], O.Option[T.Tuple2[BS, B]]]) O.Option[T.Tuple2[CS, C]] + 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[T.Tuple2[CS, C]] { + return func() O.Option[P.Pair[CS, C]] { // combine return F.Pipe1( - T.MakeTuple2(as(), bs()), + P.MakePair(as(), bs()), m, ) } } m = F.Flow2( - O.SequenceTuple2[T.Tuple2[AS, A], T.Tuple2[BS, B]], - O.Map(func(t T.Tuple2[T.Tuple2[AS, A], T.Tuple2[BS, B]]) T.Tuple2[CS, C] { - return T.MakeTuple2(recurse(t.F1.F1, t.F2.F1), f(t.F1.F2, t.F2.F2)) + 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 @@ -49,6 +49,6 @@ func ZipWith[AS ~func() O.Option[T.Tuple2[AS, A]], BS ~func() O.Option[T.Tuple2[ // 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[T.Tuple2[AS, A]], BS ~func() O.Option[T.Tuple2[BS, B]], CS ~func() O.Option[T.Tuple2[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]) +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/iterator/stateless/iterator.go b/iterator/stateless/iterator.go index adf2fcc..b53ad61 100644 --- a/iterator/stateless/iterator.go +++ b/iterator/stateless/iterator.go @@ -20,19 +20,19 @@ import ( L "github.com/IBM/fp-go/lazy" M "github.com/IBM/fp-go/monoid" O "github.com/IBM/fp-go/option" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" ) // Iterator represents a stateless, pure way to iterate over a sequence -type Iterator[U any] L.Lazy[O.Option[T.Tuple2[Iterator[U], U]]] +type Iterator[U any] L.Lazy[O.Option[P.Pair[Iterator[U], U]]] -// Next returns the [Iterator] for the next element in an iterator `T.Tuple2` -func Next[U any](m T.Tuple2[Iterator[U], U]) Iterator[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] `T.Tuple2` -func Current[U any](m T.Tuple2[Iterator[U], U]) U { +// 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) } diff --git a/iterator/stateless/scan_test.go b/iterator/stateless/scan_test.go index e39b617..3c3ea1e 100644 --- a/iterator/stateless/scan_test.go +++ b/iterator/stateless/scan_test.go @@ -19,7 +19,7 @@ import ( "testing" F "github.com/IBM/fp-go/function" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" "github.com/stretchr/testify/assert" ) @@ -29,14 +29,14 @@ func TestScan(t *testing.T) { dst := F.Pipe1( src, - Scan(func(cur T.Tuple2[int, string], val string) T.Tuple2[int, string] { - return T.MakeTuple2(cur.F1+1, val) - }, T.MakeTuple2(0, "")), + 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( - T.MakeTuple2(1, "a"), - T.MakeTuple2(2, "b"), - T.MakeTuple2(3, "c"), + P.MakePair(1, "a"), + P.MakePair(2, "b"), + P.MakePair(3, "c"), )), ToArray(dst)) } diff --git a/iterator/stateless/zip.go b/iterator/stateless/zip.go index 04f1af8..bdd603c 100644 --- a/iterator/stateless/zip.go +++ b/iterator/stateless/zip.go @@ -17,7 +17,7 @@ package stateless import ( G "github.com/IBM/fp-go/iterator/stateless/generic" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/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 @@ -28,6 +28,6 @@ func ZipWith[FCT ~func(A, B) C, A, B, C any](fa Iterator[A], fb Iterator[B], f 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[T.Tuple2[A, B]] { - return G.Zip[Iterator[A], Iterator[B], Iterator[T.Tuple2[A, B]]](fb) +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/iterator/stateless/zip_test.go b/iterator/stateless/zip_test.go index 050dc63..f4d0328 100644 --- a/iterator/stateless/zip_test.go +++ b/iterator/stateless/zip_test.go @@ -19,7 +19,7 @@ import ( "fmt" "testing" - T "github.com/IBM/fp-go/tuple" + P "github.com/IBM/fp-go/pair" "github.com/stretchr/testify/assert" ) @@ -40,5 +40,5 @@ func TestZip(t *testing.T) { res := Zip[string](left)(right) - assert.Equal(t, ToArray(From(T.MakeTuple2("a", 1), T.MakeTuple2("b", 2), T.MakeTuple2("c", 3))), ToArray(res)) + assert.Equal(t, ToArray(From(P.MakePair("a", 1), P.MakePair("b", 2), P.MakePair("c", 3))), ToArray(res)) } diff --git a/option/pair.go b/option/pair.go new file mode 100644 index 0000000..565dbd7 --- /dev/null +++ b/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/pair" + PG "github.com/IBM/fp-go/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/pair/generic/sequence.go b/pair/generic/sequence.go new file mode 100644 index 0000000..9b6f94c --- /dev/null +++ b/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/function" + P "github.com/IBM/fp-go/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/pair/pair.go b/pair/pair.go index 7f891d8..8bc9ed9 100644 --- a/pair/pair.go +++ b/pair/pair.go @@ -25,7 +25,7 @@ import ( type ( pair struct { - head, Tail any + h, t any } // Pair defines a data structure that holds two strongly typed values @@ -36,7 +36,7 @@ type ( // // go:noinline func pairString(s *pair) string { - return fmt.Sprintf("Pair[%T, %t](%v, %v)", s.head, s.Tail, s.head, s.Tail) + return fmt.Sprintf("Pair[%T, %t](%v, %v)", s.h, s.t, s.h, s.t) } // Format prints some debug info for the object @@ -63,12 +63,12 @@ func (s Pair[A, B]) Format(f fmt.State, c rune) { // 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]{head: value, Tail: value} + 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]{head: t.F1, Tail: t.F2} + return Pair[A, B]{h: t.F1, t: t.F2} } // ToTuple creates a [T.Tuple2] from a [Pair] @@ -78,22 +78,22 @@ func ToTuple[A, B any](t Pair[A, B]) T.Tuple2[A, B] { // MakePair creates a [Pair] from two values func MakePair[A, B any](a A, b B) Pair[A, B] { - return Pair[A, B]{head: a, Tail: 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.head.(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.Tail.(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.Tail} + return Pair[A1, B]{f(Head(fa)), fa.t} } // MonadMap maps the head value @@ -103,7 +103,7 @@ func MonadMap[B, A, A1 any](fa Pair[A, B], f func(A) A1) Pair[A1, B] { // 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.head, f(Tail(fa))} + return Pair[A, B1]{fa.h, f(Tail(fa))} } // MonadBiMap maps both values @@ -136,13 +136,13 @@ func BiMap[A, B, A1, B1 any](f func(A) A1, g func(B) B1) func(Pair[A, B]) Pair[A // 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.head, sg.Concat(Tail(fa), Tail(fb))} + 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.Tail} + return Pair[A, B1]{sg.Concat(Head(fb), Head(fa)), fa.t} } // MonadChain chains on the head value @@ -202,3 +202,30 @@ func ApTail[A, B, B1 any](sg Sg.Semigroup[A], fb Pair[A, B]) func(Pair[A, func(B 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)) + } +}