1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-26 19:38:58 +02:00

Compare commits

...

28 Commits

Author SHA1 Message Date
Dr. Carsten Leue
76ae6f2bec fix: more functions for StateReaderIOEither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-19 14:34:35 +01:00
Dr. Carsten Leue
f4f4fb306c fix: add conversions for StateReaderIOEither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-18 21:45:15 +01:00
Dr. Carsten Leue
747f477a96 fix: next small step towards StateReaderIOEither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-18 21:01:39 +01:00
Dr. Carsten Leue
045f4e8849 fix: add basic implementation of StateReaderIOEither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-17 23:06:52 +01:00
Dr. Carsten Leue
65e09e0e90 fix: expose Monad and Pointed for IOEither
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-16 15:54:40 +01:00
Dr. Carsten Leue
6ab6ff094b fix: correct sequence order of TraverseArraySeq for IO
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-16 15:46:12 +01:00
Dr. Carsten Leue
e6e35d643c fix: update doc
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-13 14:25:56 +01:00
Dr. Carsten Leue
01d490b710 fix: add State monad
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-13 14:23:33 +01:00
Dr. Carsten Leue
01786a054b fix: refactor Writer monad
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-13 10:44:57 +01:00
Dr. Carsten Leue
d0e4984b60 fix: switch internal implementation of iterator from Tuple2 to Pair
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-12 10:33:42 +01:00
Dr. Carsten Leue
51ed1693a5 fix: refactory tests a bit
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-09 15:06:06 +01:00
Dr. Carsten Leue
0afedbd7fe fix: generic order on ContramapCache
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-08 13:19:01 +01:00
Dr. Carsten Leue
3f1bde219a fix: add go 1.22 to test
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-08 10:08:25 +01:00
Dr. Carsten Leue
6f91e91eb9 fix: add hash to http builder
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-08 09:42:29 +01:00
Dr. Carsten Leue
9f6b6d4968 fix: optimize use of tuples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-08 08:41:51 +01:00
Dr. Carsten Leue
79652d8474 fix: disable inlining of debug functions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 13:41:47 +01:00
Dr. Carsten Leue
a774d63e66 fix: try a more efficient implementation of standard interfaces
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 13:26:17 +01:00
Dr. Carsten Leue
d86cf55a3d fix: add correct _test suffix to examples
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 11:32:14 +01:00
Carsten Leue
8150ae2a68 fix: refactor either type (#102)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 11:03:20 +01:00
Dr. Carsten Leue
7daf65effc Merge branch 'main' of github.com:IBM/fp-go 2024-02-07 10:10:50 +01:00
Dr. Carsten Leue
909f7c3bce fix: linter bugs
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-07 10:07:37 +01:00
renovate[bot]
5f0c644c6d chore(deps): update actions/setup-node action to v4.0.2 2024-02-07 09:04:33 +00:00
Dr. Carsten Leue
9b3d9c6930 fix: shrink size of DI a bit
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-06 22:13:34 +01:00
Dr. Carsten Leue
59381c1e50 fix: some internal refactorings
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-06 21:35:33 +01:00
Dr. Carsten Leue
358573cc20 fix: optimize strings package
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-06 14:43:23 +01:00
Dr. Carsten Leue
e166806d1b fix: adjust to some linter findings
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-05 13:46:12 +01:00
Dr. Carsten Leue
02ec50c91d fix: attempt to expose Monad as an interface
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-05 12:29:45 +01:00
Dr. Carsten Leue
9e04974d0e fix: add ReaderIOEither sample
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2024-02-04 22:25:51 +01:00
177 changed files with 5226 additions and 1024 deletions

View File

@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.20.x', '1.21.x']
go-version: [ '1.20.x', '1.21.x', '1.22.x']
steps:
# full checkout for semantic-release
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -60,7 +60,7 @@ jobs:
fetch-depth: 0
- name: Set up Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: ${{ env.NODE_VERSION }}

View File

@@ -58,7 +58,7 @@ func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
}
func Map[A, B any](f func(a A) B) func([]A) []B {
return F.Bind2nd(MonadMap[A, B], f)
return G.Map[[]A, []B, A, B](f)
}
func MapRef[A, B any](f func(a *A) B) func([]A) []B {

View File

@@ -147,7 +147,7 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
}
func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
return F.Bind2nd(MonadMap[GA, GB, A, B], f)
return array.Map[GA, GB](f)
}
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(int, A) B) GB {

43
array/generic/monad.go Normal file
View File

@@ -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/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]{}
}

26
array/monad.go Normal file
View File

@@ -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/array/generic"
"github.com/IBM/fp-go/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]()
}

View File

@@ -44,11 +44,11 @@ func From[A any](first A, data ...A) NonEmptyArray[A] {
return buffer
}
func IsEmpty[A any](as NonEmptyArray[A]) bool {
func IsEmpty[A any](_ NonEmptyArray[A]) bool {
return false
}
func IsNonEmpty[A any](as NonEmptyArray[A]) bool {
func IsNonEmpty[A any](_ NonEmptyArray[A]) bool {
return true
}

View File

@@ -32,20 +32,20 @@ type bounded[T any] struct {
b T
}
func (self bounded[T]) Equals(x, y T) bool {
return self.e(x, y)
func (b bounded[T]) Equals(x, y T) bool {
return b.e(x, y)
}
func (self bounded[T]) Compare(x, y T) int {
return self.c(x, y)
func (b bounded[T]) Compare(x, y T) int {
return b.c(x, y)
}
func (self bounded[T]) Top() T {
return self.t
func (b bounded[T]) Top() T {
return b.t
}
func (self bounded[T]) Bottom() T {
return self.b
func (b bounded[T]) Bottom() T {
return b.b
}
// MakeBounded creates an instance of a bounded type

View File

@@ -15,6 +15,10 @@
package bytes
func Empty() []byte {
return Monoid.Empty()
}
func ToString(a []byte) string {
return string(a)
}

View File

@@ -405,8 +405,6 @@ func generateTupleHelpers(filename string, count int) error {
fmt.Fprintf(f, `
import (
"fmt"
"encoding/json"
M "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/ord"
)
@@ -457,7 +455,7 @@ func generateTupleMarshal(f *os.File, i int) {
fmt.Fprintf(f, "func (t ")
writeTupleType(f, "T", i)
fmt.Fprintf(f, ") MarshalJSON() ([]byte, error) {\n")
fmt.Fprintf(f, " return json.Marshal([]any{")
fmt.Fprintf(f, " return tupleMarshalJSON(")
// function prototypes
for j := 1; j <= i; j++ {
if j > 1 {
@@ -465,7 +463,7 @@ func generateTupleMarshal(f *os.File, i int) {
}
fmt.Fprintf(f, "t.F%d", j)
}
fmt.Fprintf(f, "})\n")
fmt.Fprintf(f, ")\n")
fmt.Fprintf(f, "}\n")
}
@@ -475,19 +473,12 @@ func generateTupleUnmarshal(f *os.File, i int) {
fmt.Fprintf(f, "func (t *")
writeTupleType(f, "T", i)
fmt.Fprintf(f, ") UnmarshalJSON(data []byte) error {\n")
fmt.Fprintf(f, " var tmp []json.RawMessage\n")
fmt.Fprintf(f, " if err := json.Unmarshal(data, &tmp); err != nil {return err}\n")
fmt.Fprintf(f, " l := len(tmp)\n")
// unmarshal fields
fmt.Fprintf(f, " return tupleUnmarshalJSON(data")
// function prototypes
for j := 1; j <= i; j++ {
fmt.Fprintf(f, " if l > %d {\n", j-1)
fmt.Fprintf(f, " if err := json.Unmarshal(tmp[%d], &t.F%d); err != nil {return err}\n", j-1, j)
fmt.Fprintf(f, ", &t.F%d", j)
}
fmt.Fprintf(f, " ")
for j := 1; j <= i; j++ {
fmt.Fprintf(f, "}")
}
fmt.Fprintf(f, "\n return nil\n")
fmt.Fprintf(f, ")\n")
fmt.Fprintf(f, "}\n")
}
@@ -570,30 +561,13 @@ func generateTupleString(f *os.File, i int) {
writeTupleType(f, "T", i)
fmt.Fprintf(f, ") String() string {\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, ")\", ")
fmt.Fprint(f, " return tupleString(")
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")
}

View File

@@ -37,7 +37,7 @@ 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], f func(A) B) Const[E, B] {
func MonadMap[E, A, B any](fa Const[E, A], _ func(A) B) Const[E, B] {
return Make[E, B](fa.value)
}

View File

@@ -47,5 +47,5 @@ func ExampleReadFile() {
fmt.Println(result())
// Output:
// Right[<nil>, string](Carsten)
// Right[string](Carsten)
}

View File

@@ -26,7 +26,7 @@ import (
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(ctx context.Context) func() ([]byte, error) {
RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) {
return func() ([]byte, error) {
_, err := w.Write(data)
return data, err

View File

@@ -182,10 +182,7 @@ func withCancelCauseFunc[
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.MakeIO[func() any](func() any {
cancel(err)
return nil
})
return IO.FromImpure[func() any](func() { cancel(err) })
}),
IOE.Swap[func() E.Either[A, error], GIOA],
)

View File

@@ -61,7 +61,7 @@ func Requester(builder *R.Builder) RIOEH.Requester {
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.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 {

View File

@@ -32,12 +32,12 @@ import (
func TestBuilderWithQuery(t *testing.T) {
// add some query
withLimit := R.WithQueryArg("limit")("10")
withUrl := R.WithUrl("http://www.example.org?a=b")
withURL := R.WithURL("http://www.example.org?a=b")
b := F.Pipe2(
R.Default,
withLimit,
withUrl,
withURL,
)
req := F.Pipe3(

View File

@@ -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)),
)
}),
)
@@ -103,16 +103,27 @@ func ReadText(client Client) func(Requester) RIOE.ReaderIOEither[string] {
}
// 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.ChainEitherK(F.Flow2(
H.Body,
J.Unmarshal[A],
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]),
)
}

View File

@@ -31,7 +31,7 @@ import (
)
type PostItem struct {
UserId uint `json:"userId"`
UserID uint `json:"userId"`
Id uint `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
@@ -84,7 +84,7 @@ func TestSendSingleRequest(t *testing.T) {
req1 := MakeGetRequest("https://jsonplaceholder.typicode.com/posts/1")
readItem := ReadJson[PostItem](client)
readItem := ReadJSON[PostItem](client)
resp1 := readItem(req1)
@@ -112,7 +112,7 @@ func TestSendSingleRequestWithHeaderUnsafe(t *testing.T) {
R.Map(setHeaderUnsafe("Content-Type", "text/html")),
)
readItem := ReadJson[PostItem](client)
readItem := ReadJSON[PostItem](client)
resp1 := F.Pipe2(
req1,
@@ -140,7 +140,7 @@ func TestSendSingleRequestWithHeaderSafe(t *testing.T) {
WithHeader("Content-Type", "text/html").
Build()
readItem := ReadJson[PostItem](client)
readItem := ReadJSON[PostItem](client)
response := F.Pipe2(
request,

View File

@@ -127,7 +127,7 @@ func MakeInjector(providers []Provider) InjectableFactory {
var resolved sync.Map
// provide a mapping for all providers
factoryById := assembleProviders(providers)
factoryByID := assembleProviders(providers)
// the actual factory, we need lazy initialization
var injFct InjectableFactory
@@ -149,7 +149,7 @@ func MakeInjector(providers []Provider) InjectableFactory {
T.Map2(F.Flow3(
Dependency.Id,
R.Lookup[ProviderFactory, string],
I.Ap[O.Option[ProviderFactory]](factoryById),
I.Ap[O.Option[ProviderFactory]](factoryByID),
), handleMissingProvider),
T.Tupled2(O.MonadGetOrElse[ProviderFactory]),
IG.Ap[ProviderFactory](injFct),

View File

@@ -41,7 +41,7 @@ func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error,
}
func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
return func(params ...any) IOE.IOEither[error, any] {
return func(_ ...any) IOE.IOEither[error, any] {
return F.Pipe1(
f,
IOE.Map[error](F.ToAny[R]),

View File

@@ -65,34 +65,38 @@ type MultiInjectionToken[T any] interface {
}
// makeID creates a generator of unique string IDs
func makeId() IO.IO[string] {
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()
// genID is the common generator of unique string IDs
var genID = makeID()
type token[T any] struct {
type tokenBase struct {
name string
id string
flag int
toType func(val any) E.Either[error, T]
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.id
return t.base.id
}
func (t *token[T]) Flag() int {
return t.flag
return t.base.flag
}
func (t *token[T]) String() string {
return t.name
return t.base.name
}
func (t *token[T]) Unerase(val any) E.Either[error, T] {
@@ -100,11 +104,14 @@ func (t *token[T]) Unerase(val any) E.Either[error, T] {
}
func (t *token[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
return t.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]{name, id, typ, unerase, providerFactory}
return &token[T]{makeTokenBase(name, id, typ, providerFactory), unerase}
}
type injectionToken[T any] struct {
@@ -136,7 +143,7 @@ func (i *injectionToken[T]) IOOption() Dependency[IOO.IOOption[T]] {
}
func (i *injectionToken[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
return i.providerFactory
return i.base.providerFactory
}
func (m *multiInjectionToken[T]) Container() InjectionToken[[]T] {
@@ -149,10 +156,10 @@ func (m *multiInjectionToken[T]) Item() InjectionToken[T] {
// makeToken create a unique [InjectionToken] for a specific type
func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] {
id := genId()
id := genID()
toIdentity := toType[T]()
return &injectionToken[T]{
token[T]{name, id, DIE.Identity, toIdentity, providerFactory},
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),
@@ -171,7 +178,7 @@ func MakeTokenWithDefault[T any](name string, providerFactory DIE.ProviderFactor
// MakeMultiToken creates a [MultiInjectionToken]
func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
id := genId()
id := genID()
toItem := toType[T]()
toContainer := toArrayType(toItem)
containerName := fmt.Sprintf("Container[%s]", name)
@@ -180,14 +187,14 @@ func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
providerFactory := O.None[DIE.ProviderFactory]()
// container
container := &injectionToken[[]T]{
token[[]T]{containerName, id, DIE.Multi | DIE.Identity, toContainer, providerFactory},
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]{itemName, id, DIE.Item | DIE.Identity, toItem, providerFactory},
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),

View File

@@ -25,6 +25,13 @@ import (
O "github.com/IBM/fp-go/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
@@ -38,7 +45,7 @@ func toType[T any]() func(t any) E.Either[error, T] {
// 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(
toType[O.Option[any]](),
toOptionAny,
E.Chain(O.Fold(
F.Nullary2(O.None[T], E.Of[error, O.Option[T]]),
F.Flow2(
@@ -52,7 +59,7 @@ func toOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either
// 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(
toType[IOE.IOEither[error, any]](),
toIOEitherAny,
E.Map[error](IOE.ChainEitherK(item)),
)
}
@@ -60,7 +67,7 @@ func toIOEitherType[T any](item func(any) E.Either[error, T]) func(t any) E.Eith
// 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(
toType[IOO.IOOption[any]](),
toIOOptionAny,
E.Map[error](IOO.ChainOptionK(F.Flow2(
item,
E.ToOption[error, T],
@@ -71,7 +78,7 @@ func toIOOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Eith
// 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(
toType[[]any](),
toArrayAny,
E.Chain(E.TraverseArray(item)),
)
}

View File

@@ -20,30 +20,45 @@ import (
)
type (
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
Either[E, A any] struct {
either struct {
isLeft bool
left E
right A
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
func (s Either[E, A]) String() string {
//
// go:noinline
func eitherString(s *either) string {
if s.isLeft {
return fmt.Sprintf("Left[%T, %T](%v)", s.left, s.right, s.left)
return fmt.Sprintf("Left[%T](%v)", s.value, s.value)
}
return fmt.Sprintf("Right[%T, %T](%v)", s.left, s.right, s.right)
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) {
switch c {
case 's':
fmt.Fprint(f, s.String())
default:
fmt.Fprint(f, s.String())
}
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].
@@ -58,23 +73,29 @@ func IsRight[E, A any](val Either[E, A]) bool {
// 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]{isLeft: true, left: value}
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]{isLeft: false, right: value}
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.left)
return onLeft(ma.value.(E))
}
return onRight(ma.right)
return onRight(ma.value.(A))
}
// Unwrap converts an [Either] into the idiomatic tuple
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
return ma.right, ma.left
if ma.isLeft {
var a A
return a, ma.value.(E)
} else {
var e E
return ma.value.(A), e
}
}

View File

@@ -22,6 +22,7 @@ package either
import (
E "github.com/IBM/fp-go/errors"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/internal/chain"
FC "github.com/IBM/fp-go/internal/functor"
L "github.com/IBM/fp-go/lazy"
O "github.com/IBM/fp-go/option"
@@ -85,12 +86,15 @@ func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E
}
func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] {
return MonadChain(ma, func(a A) Either[E, A] {
return MonadMap(f(a), F.Constant1[B](a))
})
return C.MonadChainFirst(
MonadChain[E, A, A],
MonadMap[E, B, A],
ma,
f,
)
}
func MonadChainTo[A, E, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
func MonadChainTo[A, E, B any](_ Either[E, A], mb Either[E, B]) Either[E, B] {
return mb
}
@@ -114,7 +118,11 @@ func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B
}
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
return F.Bind2nd(MonadChainFirst[E, A, B], f)
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] {

View File

@@ -17,6 +17,7 @@ package either
import (
"errors"
"fmt"
"testing"
F "github.com/IBM/fp-go/function"
@@ -26,12 +27,6 @@ import (
"github.com/stretchr/testify/assert"
)
func TestDefault(t *testing.T) {
var e Either[error, string]
assert.Equal(t, Of[error](""), e)
}
func TestIsLeft(t *testing.T) {
err := errors.New("Some error")
withError := Left[string](err)
@@ -115,3 +110,13 @@ 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())
}

View File

@@ -48,11 +48,11 @@ func ExampleEither_creation() {
fmt.Println(rightFromPred)
// Output:
// Left[*errors.errorString, string](some error)
// Right[<nil>, string](value)
// Left[*errors.errorString, *string](value was nil)
// Left[*errors.errorString](some error)
// Right[string](value)
// Left[*errors.errorString](value was nil)
// true
// Left[*errors.errorString, int](3 is an odd number)
// Right[<nil>, int](4)
// Left[*errors.errorString](3 is an odd number)
// Right[int](4)
}

View File

@@ -53,8 +53,8 @@ func ExampleEither_extraction() {
fmt.Println(doubleFromRightBis)
// Output:
// Left[*errors.errorString, int](Division by Zero!)
// Right[<nil>, int](10)
// Left[*errors.errorString](Division by Zero!)
// Right[int](10)
// 0
// 10
// 0

43
either/monad.go Normal file
View File

@@ -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/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]{}
}

View File

@@ -19,38 +19,17 @@ import (
F "github.com/IBM/fp-go/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<Either[B]>
HKTA = HKT<A>
HKTB = HKT<B>
*/
func traverse[E, A, B, HKTB, HKTRB any](
mof func(Either[E, B]) HKTRB,
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
) func(Either[E, A], func(A) HKTB) HKTRB {
left := F.Flow2(Left[B, E], mof)
right := mmap(Right[E, B])
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
return MonadFold(ta,
left,
F.Flow2(f, right),
)
}
}
// 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 {
delegate := traverse[E, A, B](mof, mmap)
left := F.Flow2(Left[B, E], mof)
right := mmap(Right[E, B])
return func(f func(A) HKTB) func(Either[E, A]) HKTRB {
return F.Bind2nd(delegate, f)
return Fold(left, F.Flow2(f, right))
}
}

View File

@@ -27,8 +27,8 @@ type eq[T any] struct {
c func(x, y T) bool
}
func (self eq[T]) Equals(x, y T) bool {
return self.c(x, y)
func (e eq[T]) Equals(x, y T) bool {
return e.c(x, y)
}
func strictEq[A comparable](a, b A) bool {

View File

@@ -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]
)

View File

@@ -25,13 +25,13 @@ func Memoize[K comparable, T any](f func(K) T) func(K) T {
}
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
func ContramapMemoize[A any, K comparable, T any](kf func(A) K) func(func(A) T) func(A) T {
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[
A any, K comparable, T any](kf func(A) K, getOrCreate func(K, func() func() T) func() T) func(func(A) T) func(A) T {
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)
}

View File

@@ -16,9 +16,14 @@
package builder
import (
"bytes"
"crypto/sha256"
"fmt"
"net/http"
"net/url"
A "github.com/IBM/fp-go/array"
B "github.com/IBM/fp-go/bytes"
E "github.com/IBM/fp-go/either"
ENDO "github.com/IBM/fp-go/endomorphism"
F "github.com/IBM/fp-go/function"
@@ -29,6 +34,7 @@ import (
LZ "github.com/IBM/fp-go/lazy"
L "github.com/IBM/fp-go/optics/lens"
O "github.com/IBM/fp-go/option"
R "github.com/IBM/fp-go/record"
S "github.com/IBM/fp-go/string"
T "github.com/IBM/fp-go/tuple"
)
@@ -56,7 +62,11 @@ var (
Monoid = ENDO.Monoid[*Builder]()
// Url is a [L.Lens] for the URL
Url = L.MakeLensRef((*Builder).GetUrl, (*Builder).SetUrl)
//
// 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
@@ -76,15 +86,19 @@ var (
noBody = O.None[E.Either[error, []byte]]()
noQueryArg = O.None[string]()
parseUrl = E.Eitherize1(url.Parse)
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 a certain method
WithUrl = Url.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
@@ -130,6 +144,9 @@ var (
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 {
@@ -148,12 +165,19 @@ func (builder *Builder) clone() *Builder {
}
// 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,
parseURL,
E.Chain(F.Flow4(
T.Replicate2[*url.URL],
T.Map2(
@@ -176,10 +200,15 @@ func (builder *Builder) GetTargetUrl() E.Either[error, 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,
@@ -209,11 +238,17 @@ func (builder *Builder) SetMethod(method string) *Builder {
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
@@ -246,6 +281,11 @@ 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)
@@ -278,14 +318,21 @@ func WithoutHeader(name string) Endomorphism {
}
// 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),
WithContentType(C.JSON),
)
}
@@ -309,3 +356,32 @@ func WithQueryArg(name string) func(value string) Endomorphism {
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)))
}

View File

@@ -16,6 +16,7 @@
package builder
import (
"fmt"
"testing"
F "github.com/IBM/fp-go/function"
@@ -34,7 +35,7 @@ func TestBuilder(t *testing.T) {
b1 := F.Pipe1(
Default,
withContentType(C.Json),
withContentType(C.JSON),
)
b2 := F.Pipe1(
@@ -48,7 +49,7 @@ func TestBuilder(t *testing.T) {
)
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.JSON), b1.GetHeader(name))
assert.Equal(t, O.Of(C.TextPlain), b2.GetHeader(name))
assert.Equal(t, O.None[string](), b3.GetHeader(name))
}
@@ -66,3 +67,27 @@ func TestWithFormData(t *testing.T) {
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))
}

View File

@@ -17,6 +17,7 @@ package content
const (
TextPlain = "text/plain"
Json = "application/json"
JSON = "application/json"
Json = JSON // Deprecated: use [JSON] instead
FormEncoded = "application/x-www-form-urlencoded"
)

View File

@@ -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]
)

View File

@@ -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,29 +45,31 @@ 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(
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
ValidateJsonResponse = F.Flow2(
// 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),
E.ChainFirst(validateJSONContentTypeString),
)))
// ValidateJsonResponse checks if an HTTP response is a valid JSON response
//
// Deprecated: use [ValidateJSONResponse] instead
ValidateJsonResponse = ValidateJSONResponse
)
const (
@@ -77,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

View File

@@ -39,7 +39,7 @@ func Error[A any](t *testing.T) func(E.Either[error, A]) bool {
func TestValidateJsonContentTypeString(t *testing.T) {
res := F.Pipe1(
validateJsonContentTypeString(C.Json),
validateJSONContentTypeString(C.JSON),
NoError[ParsedMediaType](t),
)
@@ -49,7 +49,7 @@ func TestValidateJsonContentTypeString(t *testing.T) {
func TestValidateInvalidJsonContentTypeString(t *testing.T) {
res := F.Pipe1(
validateJsonContentTypeString("application/xml"),
validateJSONContentTypeString("application/xml"),
Error[ParsedMediaType](t),
)

View File

@@ -36,7 +36,7 @@ func Map[A, B any](f func(A) B) func(A) B {
return G.Map(f)
}
func MonadMapTo[A, B any](fa A, b B) B {
func MonadMapTo[A, B any](_ A, b B) B {
return b
}

43
identity/monad.go Normal file
View File

@@ -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/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]{}
}

View File

@@ -20,13 +20,19 @@ import (
E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/applicative"
"github.com/IBM/fp-go/internal/apply"
L "github.com/IBM/fp-go/internal/apply/testing"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/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],
@@ -34,6 +40,9 @@ func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
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)
@@ -43,9 +52,33 @@ func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
}
}
// 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],
@@ -57,6 +90,9 @@ func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
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))
@@ -66,14 +102,39 @@ func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
}
}
// 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,
fofb func(B) HKTB,
fofab func(func(A) B) HKTAB,
fofabb func(func(func(A) B) B) HKTABB,
@@ -82,6 +143,9 @@ func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
ab func(A) B,
) func(a A) bool {
// mark as test helper
t.Helper()
return func(a A) bool {
fab := fofab(ab)
@@ -95,7 +159,38 @@ func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
}
}
// 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],
@@ -127,15 +222,62 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
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, fofb, fofab, fofabb, fapab, fapabb, 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)
}
}

View File

@@ -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/internal/apply"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/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
}

View File

@@ -19,13 +19,18 @@ import (
"testing"
E "github.com/IBM/fp-go/eq"
"github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/functor"
FCT "github.com/IBM/fp-go/internal/functor/testing"
"github.com/IBM/fp-go/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],
@@ -43,6 +48,7 @@ func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC
ab func(A) B,
bc func(B) C,
) func(fa HKTA) bool {
t.Helper()
return func(fa HKTA) bool {
fab := fofab(ab)
@@ -62,7 +68,49 @@ func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC
}
}
// 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],
@@ -86,6 +134,8 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
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
@@ -95,3 +145,36 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
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)
}
}

30
internal/apply/types.go Normal file
View File

@@ -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/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
}

View File

@@ -106,6 +106,12 @@ func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
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)

View File

@@ -20,17 +20,22 @@ import (
E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/apply"
L "github.com/IBM/fp-go/internal/apply/testing"
"github.com/IBM/fp-go/internal/chain"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/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],
fofa func(A) HKTA,
fofb func(B) HKTB,
fofc func(C) HKTC,
@@ -56,12 +61,44 @@ func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
}
}
// 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],
fofa func(A) HKTA,
fofb func(B) HKTB,
fofc func(C) HKTC,
@@ -91,7 +128,41 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
// 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, fofa, fofb, fofc, chainab, chainac, chainbc, ab, bc)
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)

36
internal/chain/types.go Normal file
View File

@@ -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/internal/apply"
"github.com/IBM/fp-go/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
}

View File

@@ -159,3 +159,7 @@ func OrLeft[E1, E2, A, HKTE1A, HKTE2, HKTE2A any](
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)
}

View File

@@ -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
}

View File

@@ -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/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
}

View File

@@ -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/either"
)
type FromEither[E, A, HKTA any] interface {
FromEither(ET.Either[E, A]) HKTA
}

20
internal/fromio/types.go Normal file
View File

@@ -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
}

View File

@@ -20,21 +20,44 @@ import (
E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/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,
@@ -46,12 +69,36 @@ func AssertComposition[HKTA, HKTB, HKTC, A, B, C any](
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],
@@ -60,9 +107,11 @@ func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
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)
@@ -70,3 +119,25 @@ func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
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)
}
}

20
internal/functor/types.go Normal file
View File

@@ -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
}

54
internal/monad/monad.go Normal file
View File

@@ -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/internal/applicative"
"github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/chain"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/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
}

View File

@@ -19,14 +19,21 @@ import (
"testing"
E "github.com/IBM/fp-go/eq"
"github.com/IBM/fp-go/internal/applicative"
LA "github.com/IBM/fp-go/internal/applicative/testing"
"github.com/IBM/fp-go/internal/chain"
LC "github.com/IBM/fp-go/internal/chain/testing"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/monad"
"github.com/IBM/fp-go/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],
@@ -50,9 +57,36 @@ func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T,
}
}
// 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],
@@ -69,7 +103,27 @@ func AssertRightIdentity[HKTA, A any](t *testing.T,
}
}
// 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],
@@ -110,7 +164,7 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
// 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, fofa, fofb, fofc, fofab, fofbc, faa, fab, fac, fbc, fmap, chainab, chainac, chainbc, fapab, fapbc, fapac, fapabac, ab, bc)
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)
@@ -120,3 +174,55 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, 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)
}
}

View File

@@ -40,6 +40,12 @@ func MonadMap[A, B, HKTFA, HKTFB any](fmap func(HKTFA, func(O.Option[A]) O.Optio
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,

21
internal/pointed/types.go Normal file
View File

@@ -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
}

198
internal/statet/state.go Normal file
View File

@@ -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/function"
P "github.com/IBM/fp-go/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)
}

View File

@@ -27,16 +27,11 @@ const (
// 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 MakeIO[GB](func() B {
return F.Pipe1(
ma(),
mab(),
)
})
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 thred computes ma. Communication between the threads happens via a channel
// 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)

View File

@@ -25,6 +25,11 @@ import (
T "github.com/IBM/fp-go/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 {
@@ -43,7 +48,7 @@ func FromIO[GA ~func() A, A any](a GA) GA {
func FromImpure[GA ~func() any, IMP ~func()](f IMP) GA {
return MakeIO[GA](func() any {
f()
return nil
return undefined
})
}

43
io/generic/monad.go Normal file
View File

@@ -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/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]{}
}

View File

@@ -32,6 +32,28 @@ func MonadTraverseArray[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B
)
}
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],
@@ -42,6 +64,26 @@ func TraverseArray[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A, B any](
)
}
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],
@@ -52,10 +94,38 @@ func TraverseArrayWithIndex[GB ~func() B, GBS ~func() BBS, AAS ~[]A, BBS ~[]B, A
)
}
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](
@@ -89,3 +159,71 @@ func TraverseRecordWithIndex[GB ~func() B, GBS ~func() MB, MA ~map[K]A, MB ~map[
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])
}

View File

@@ -27,7 +27,7 @@ func TestLogger(t *testing.T) {
lio := l("out")
assert.Equal(t, nil, lio(10)())
assert.NotPanics(t, func() { lio(10)() })
}
func TestLogf(t *testing.T) {
@@ -36,5 +36,5 @@ func TestLogf(t *testing.T) {
lio := l("Value is %d")
assert.Equal(t, nil, lio(10)())
assert.NotPanics(t, func() { lio(10)() })
}

26
io/monad.go Normal file
View File

@@ -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/internal/monad"
G "github.com/IBM/fp-go/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]]()
}

47
io/sequence_test.go Normal file
View File

@@ -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 (
A "github.com/IBM/fp-go/array"
F "github.com/IBM/fp-go/function"
"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())
}

View File

@@ -60,3 +60,45 @@ func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) IO[B]) func(ma
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)
}

View File

@@ -48,10 +48,10 @@ func ExampleIOEither_creation() {
fmt.Println(rightFromPred())
// Output:
// Left[*errors.errorString, string](some error)
// Right[<nil>, string](value)
// Right[<nil>, int](42)
// Left[*errors.errorString, int](3 is an odd number)
// Right[<nil>, int](4)
// Left[*errors.errorString](some error)
// Right[string](value)
// Right[int](42)
// Left[*errors.errorString](3 is an odd number)
// Right[int](4)
}

View File

@@ -53,5 +53,5 @@ func ExampleIOEither_do() {
fmt.Println(b())
// Output:
// Right[<nil>, int](8)
// Right[int](8)
}

View File

@@ -38,7 +38,7 @@ func ExampleIOEither_extraction() {
fmt.Println(valueFromIO)
// Output:
// Right[<nil>, int](42)
// Right[int](42)
// 42
// 42

View File

@@ -156,7 +156,10 @@ func MonadAp[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B], GA
}
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 F.Bind2nd(MonadAp[GB, GAB, GA], ma)
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 {
@@ -167,7 +170,10 @@ func MonadApSeq[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B],
}
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 F.Bind2nd(MonadApSeq[GB, GAB, GA], ma)
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 {
@@ -178,7 +184,10 @@ func MonadApPar[GB ~func() ET.Either[E, B], GAB ~func() ET.Either[E, func(A) B],
}
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 F.Bind2nd(MonadApPar[GB, GAB, GA], ma)
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 {
@@ -204,11 +213,18 @@ func Memoize[GA ~func() ET.Either[E, A], E, A any](ma GA) GA {
}
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)
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 F.Bind2nd(MonadMapLeft[GA1, GA2, E1, E2, A], f)
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]

View File

@@ -24,9 +24,9 @@ import (
F "github.com/IBM/fp-go/function"
)
// LogJson converts the argument to JSON and then logs it via the format string
// 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 {
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(
@@ -41,3 +41,11 @@ func LogJson[GA ~func() ET.Either[error, any], A any](prefix string) func(A) GA
)
}
}
// 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)
}

56
ioeither/generic/monad.go Normal file
View File

@@ -0,0 +1,56 @@
// 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/either"
"github.com/IBM/fp-go/internal/monad"
"github.com/IBM/fp-go/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{}
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)
}
// 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]{}
}
// 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]{}
}

View File

@@ -56,7 +56,7 @@ func Requester(builder *R.Builder) IOEH.Requester {
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.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 {

View File

@@ -31,12 +31,12 @@ import (
func TestBuilderWithQuery(t *testing.T) {
// add some query
withLimit := R.WithQueryArg("limit")("10")
withUrl := R.WithUrl("http://www.example.org?a=b")
withURL := R.WithUrl("http://www.example.org?a=b")
b := F.Pipe2(
R.Default,
withLimit,
withUrl,
withURL,
)
req := F.Pipe3(

View File

@@ -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)),
)
}),
)
@@ -118,16 +118,28 @@ func ReadText(client Client) func(Requester) IOE.IOEither[error, string] {
}
// 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.ChainEitherK(F.Flow2(
H.Body,
J.Unmarshal[A],
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]),
)
}

View File

@@ -40,7 +40,7 @@ var testLogPolicy = R.CapDelay(
)
type PostItem struct {
UserId uint `json:"userId"`
UserID uint `json:"userId"`
Id uint `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
@@ -54,7 +54,7 @@ func TestRetryHttp(t *testing.T) {
action := func(status R.RetryStatus) IOE.IOEither[error, *PostItem] {
return F.Pipe1(
MakeGetRequest(urls[status.IterNumber]),
ReadJson[*PostItem](client),
ReadJSON[*PostItem](client),
)
}

View File

@@ -21,6 +21,14 @@ import (
// 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)
}

View File

@@ -34,7 +34,7 @@ func TestLogging(t *testing.T) {
res := F.Pipe1(
Of[error](src),
ChainFirst(LogJson[*SomeData]("Data: \n%s")),
ChainFirst(LogJSON[*SomeData]("Data: \n%s")),
)
dst := res()

32
ioeither/monad.go Normal file
View File

@@ -0,0 +1,32 @@
// 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/internal/monad"
"github.com/IBM/fp-go/internal/pointed"
G "github.com/IBM/fp-go/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]]()
}
// 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]]()
}

48
ioeither/sequence_test.go Normal file
View File

@@ -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 (
A "github.com/IBM/fp-go/array"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
"github.com/stretchr/testify/assert"
"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())
}

View File

@@ -70,7 +70,7 @@ func MonadMap[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](fa GA, f
}
func Map[GA ~func() O.Option[A], GB ~func() O.Option[B], A, B any](f func(A) B) func(GA) GB {
return F.Bind2nd(MonadMap[GA, GB, A, B], f)
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 {

View File

@@ -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)
}

View File

@@ -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],

View File

@@ -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 {

View File

@@ -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]),
)),
)
}

View File

@@ -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)

View File

@@ -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]]),
)
})

View File

@@ -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]),
)
}

View File

@@ -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]],
)),
)
}

View File

@@ -18,50 +18,51 @@ package generic
import (
A "github.com/IBM/fp-go/array/generic"
F "github.com/IBM/fp-go/function"
C "github.com/IBM/fp-go/internal/chain"
"github.com/IBM/fp-go/internal/utils"
IO "github.com/IBM/fp-go/iooption/generic"
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 {
@@ -73,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(
@@ -93,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)
@@ -112,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(
@@ -131,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()
},
),
@@ -142,18 +143,35 @@ 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 Flatten[GV ~func() O.Option[T.Tuple2[GV, GU]], GU ~func() O.Option[T.Tuple2[GU, U]], U any](ma 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],
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[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(
@@ -163,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
@@ -176,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],
@@ -191,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(
@@ -208,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]],
))
},
)
@@ -220,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))
@@ -247,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())
}

View File

@@ -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]())
}

View File

@@ -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/internal/monad"
O "github.com/IBM/fp-go/option"
P "github.com/IBM/fp-go/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]{}
}

View File

@@ -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](),

View File

@@ -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),
),
)),
)

View File

@@ -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],
)),
)

View File

@@ -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])),
))),
)
}

View File

@@ -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)
}

View File

@@ -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])
}

View File

@@ -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)
}
@@ -144,3 +144,11 @@ func FoldMap[U, V any](m M.Monoid[V]) func(func(U) V) func(ma Iterator[U]) V {
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)
}

View File

@@ -67,8 +67,8 @@ func isPrimeNumber(num int) bool {
if num <= 2 {
return true
}
sq_root := int(math.Sqrt(float64(num)))
for i := 2; i <= sq_root; i++ {
sqRoot := int(math.Sqrt(float64(num)))
for i := 2; i <= sqRoot; i++ {
if num%i == 0 {
return false
}

Some files were not shown because too many files have changed in this diff Show More