1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-10 22:31:32 +02:00

fix: refactory tests a bit

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2024-02-09 15:06:06 +01:00
parent 0afedbd7fe
commit 51ed1693a5
20 changed files with 1179 additions and 16 deletions

View File

@@ -26,7 +26,7 @@ import (
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] { func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
return func(w W) RIOE.ReaderIOEither[[]byte] { return func(w W) RIOE.ReaderIOEither[[]byte] {
return F.Pipe1( return F.Pipe1(
RIOE.TryCatch(func(ctx context.Context) func() ([]byte, error) { RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) {
return func() ([]byte, error) { return func() ([]byte, error) {
_, err := w.Write(data) _, err := w.Write(data)
return data, err return data, err

View File

@@ -109,17 +109,21 @@ func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
return ReadJSON[A](client) return ReadJSON[A](client)
} }
// ReadJSON sends a request, reads the response and parses the response as JSON func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
return F.Flow3( return F.Flow3(
ReadFullResponse(client), ReadFullResponse(client),
RIOE.ChainFirstEitherK(F.Flow2( RIOE.ChainFirstEitherK(F.Flow2(
H.Response, H.Response,
H.ValidateJSONResponse, H.ValidateJSONResponse,
)), )),
RIOE.ChainEitherK(F.Flow2( RIOE.Map(H.Body),
H.Body, )
J.Unmarshal[A], }
)),
// 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

@@ -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] { 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( return F.Pipe1(
f, f,
IOE.Map[error](F.ToAny[R]), IOE.Map[error](F.ToAny[R]),

View File

@@ -36,7 +36,7 @@ func Map[A, B any](f func(A) B) func(A) B {
return G.Map(f) 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 return b
} }

View File

@@ -20,13 +20,19 @@ import (
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function" 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" 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" "github.com/stretchr/testify/assert"
) )
// Applicative identity law // Applicative identity law
// //
// A.ap(A.of(a => a), fa) <-> fa // A.ap(A.of(a => a), fa) <-> fa
//
// Deprecated: use [ApplicativeAssertIdentity]
func AssertIdentity[HKTA, HKTAA, A any](t *testing.T, func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
eq E.Eq[HKTA], eq E.Eq[HKTA],
@@ -46,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 // Applicative homomorphism law
// //
// A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a)) // 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, func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
eq E.Eq[HKTB], eq E.Eq[HKTB],
@@ -72,9 +102,35 @@ 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 // Applicative interchange law
// //
// A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab) // 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, func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
eq E.Eq[HKTB], eq E.Eq[HKTB],
@@ -103,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' // 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, func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqb E.Eq[HKTB], eqb E.Eq[HKTB],
@@ -150,3 +237,47 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
return apply(fa) && identity(fa) && homomorphism(a) && interchange(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

@@ -17,6 +17,7 @@ package applicative
import ( import (
"github.com/IBM/fp-go/internal/apply" "github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/pointed" "github.com/IBM/fp-go/internal/pointed"
) )
@@ -24,3 +25,18 @@ type Applicative[A, B, HKTA, HKTB, HKTFAB any] interface {
apply.Apply[A, B, HKTA, HKTB, HKTFAB] apply.Apply[A, B, HKTA, HKTB, HKTFAB]
pointed.Pointed[A, HKTA] 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" "testing"
E "github.com/IBM/fp-go/eq" 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" FCT "github.com/IBM/fp-go/internal/functor/testing"
"github.com/IBM/fp-go/internal/pointed"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Apply associative composition law // 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)) // 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, func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eq E.Eq[HKTC], eq E.Eq[HKTC],
@@ -63,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` // 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, func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqc E.Eq[HKTC], eqc E.Eq[HKTC],
@@ -98,3 +145,36 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
return functor(fa) && composition(fa) 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)
}
}

View File

@@ -23,3 +23,8 @@ type Apply[A, B, HKTA, HKTB, HKTFAB any] interface {
functor.Functor[A, B, HKTA, HKTB] functor.Functor[A, B, HKTA, HKTB]
Ap(HKTA) func(HKTFAB) 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

@@ -20,13 +20,19 @@ import (
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/apply"
L "github.com/IBM/fp-go/internal/apply/testing" 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" "github.com/stretchr/testify/assert"
) )
// Chain associativity law // Chain associativity law
// //
// F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc)) // 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, func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
eq E.Eq[HKTC], eq E.Eq[HKTC],
@@ -55,7 +61,40 @@ 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` // 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, func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqc E.Eq[HKTC], eqc E.Eq[HKTC],
@@ -95,3 +134,37 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
return apply(fa) && associativity(fa) 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)
}
}

View File

@@ -17,9 +17,20 @@ package chain
import ( import (
"github.com/IBM/fp-go/internal/apply" "github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/functor"
) )
type Chainable[A, B, HKTA, HKTB, HKTFAB any] interface { type Chainable[A, B, HKTA, HKTB, HKTFAB any] interface {
apply.Apply[A, B, HKTA, HKTB, HKTFAB] apply.Apply[A, B, HKTA, HKTB, HKTFAB]
Chain(func(A) HKTB) func(HKTA) HKTB 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

@@ -20,12 +20,15 @@ import (
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
F "github.com/IBM/fp-go/function" F "github.com/IBM/fp-go/function"
"github.com/IBM/fp-go/internal/functor"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Functor identity law // Functor identity law
// //
// F.map(fa, a => a) <-> fa // 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 { 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() t.Helper()
return func(fa HKTA) bool { return func(fa HKTA) bool {
@@ -33,9 +36,28 @@ func AssertIdentity[HKTA, A any](t *testing.T, eq E.Eq[HKTA], fmap func(HKTA, fu
} }
} }
// 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 // Functor composition law
// //
// F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc) // 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]( func AssertComposition[HKTA, HKTB, HKTC, A, B, C any](
t *testing.T, t *testing.T,
@@ -53,7 +75,30 @@ func AssertComposition[HKTA, HKTB, HKTC, A, B, C any](
} }
} }
// 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` // AssertLaws asserts the functor laws `identity` and `composition`
//
// Deprecated: use [FunctorAssertLaws] instead
func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T, func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqc E.Eq[HKTC], eqc E.Eq[HKTC],
@@ -62,6 +107,7 @@ func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
fab func(HKTA, func(A) B) HKTB, fab func(HKTA, func(A) B) HKTB,
fac func(HKTA, func(A) C) HKTC, fac func(HKTA, func(A) C) HKTC,
fbc func(HKTB, func(B) C) HKTC, fbc func(HKTB, func(B) C) HKTC,
ab func(A) B, ab func(A) B,
bc func(B) C, bc func(B) C,
) func(fa HKTA) bool { ) func(fa HKTA) bool {
@@ -73,3 +119,25 @@ func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
return identity(fa) && composition(fa) 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)
}
}

View File

@@ -17,10 +17,38 @@ package monad
import ( import (
"github.com/IBM/fp-go/internal/applicative" "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/chain"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/pointed"
) )
type Monad[A, B, HKTA, HKTB, HKTFAB any] interface { type Monad[A, B, HKTA, HKTB, HKTFAB any] interface {
applicative.Applicative[A, B, HKTA, HKTB, HKTFAB] applicative.Applicative[A, B, HKTA, HKTB, HKTFAB]
chain.Chainable[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" "testing"
E "github.com/IBM/fp-go/eq" E "github.com/IBM/fp-go/eq"
"github.com/IBM/fp-go/internal/applicative"
LA "github.com/IBM/fp-go/internal/applicative/testing" 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" 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" "github.com/stretchr/testify/assert"
) )
// Apply monad left identity law // Apply monad left identity law
// //
// M.chain(M.of(a), f) <-> f(a) // M.chain(M.of(a), f) <-> f(a)
//
// Deprecated: use [MonadAssertLeftIdentity] instead
func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T, func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T,
eq E.Eq[HKTB], 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 // Apply monad right identity law
// //
// M.chain(fa, M.of) <-> fa // M.chain(fa, M.of) <-> fa
//
// Deprecated: use [MonadAssertRightIdentity] instead
func AssertRightIdentity[HKTA, A any](t *testing.T, func AssertRightIdentity[HKTA, A any](t *testing.T,
eq E.Eq[HKTA], 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` // 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, func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA], eqa E.Eq[HKTA],
eqb E.Eq[HKTB], eqb E.Eq[HKTB],
@@ -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) 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

@@ -124,17 +124,22 @@ func ReadJson[A any](client Client) func(Requester) IOE.IOEither[error, A] {
return ReadJSON[A](client) return ReadJSON[A](client)
} }
// ReadJSON sends a request, reads the response and parses the response as JSON // readJSON sends a request, reads the response and parses the response as a []byte
func ReadJSON[A any](client Client) func(Requester) IOE.IOEither[error, A] { func readJSON(client Client) func(Requester) IOE.IOEither[error, []byte] {
return F.Flow3( return F.Flow3(
ReadFullResponse(client), ReadFullResponse(client),
IOE.ChainFirstEitherK(F.Flow2( IOE.ChainFirstEitherK(F.Flow2(
H.Response, H.Response,
H.ValidateJSONResponse, H.ValidateJSONResponse,
)), )),
IOE.ChainEitherK(F.Flow2( IOE.Map[error](H.Body),
H.Body, )
J.Unmarshal[A], }
)),
// 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

@@ -113,7 +113,7 @@ func fromPredicate[S, A any](creator func(get func(S) O.Option[A], set func(S, A
return func(get func(S) A, set func(S, A) S) Optional[S, A] { return func(get func(S) A, set func(S, A) S) Optional[S, A] {
return creator( return creator(
F.Flow2(get, fromPred), F.Flow2(get, fromPred),
func(s S, a A) S { func(s S, _ A) S {
return F.Pipe3( return F.Pipe3(
s, s,
get, get,

33
pair/eq.go Normal file
View File

@@ -0,0 +1,33 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pair
import (
EQ "github.com/IBM/fp-go/eq"
)
// Constructs an equal predicate for an `Either`
func Eq[A, B any](a EQ.Eq[A], b EQ.Eq[B]) EQ.Eq[Pair[A, B]] {
return EQ.FromEquals(func(l, r Pair[A, B]) bool {
return a.Equals(Head(l), Head(r)) && b.Equals(Tail(l), Tail(r))
})
}
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
func FromStrictEquals[A, B comparable]() EQ.Eq[Pair[A, B]] {
return Eq(EQ.FromStrictEquals[A](), EQ.FromStrictEquals[B]())
}

193
pair/monad.go Normal file
View File

@@ -0,0 +1,193 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pair
import (
"github.com/IBM/fp-go/internal/applicative"
"github.com/IBM/fp-go/internal/functor"
"github.com/IBM/fp-go/internal/monad"
"github.com/IBM/fp-go/internal/pointed"
M "github.com/IBM/fp-go/monoid"
Sg "github.com/IBM/fp-go/semigroup"
)
type (
pairPointedHead[A, B any] struct {
m M.Monoid[B]
}
pairFunctorHead[A, B, A1 any] struct {
}
pairApplicativeHead[A, B, A1 any] struct {
s Sg.Semigroup[B]
m M.Monoid[B]
}
pairMonadHead[A, B, A1 any] struct {
s Sg.Semigroup[B]
m M.Monoid[B]
}
pairPointedTail[A, B any] struct {
m M.Monoid[A]
}
pairFunctorTail[A, B, B1 any] struct {
}
pairApplicativeTail[A, B, B1 any] struct {
s Sg.Semigroup[A]
m M.Monoid[A]
}
pairMonadTail[A, B, B1 any] struct {
s Sg.Semigroup[A]
m M.Monoid[A]
}
)
func (o *pairMonadHead[A, B, A1]) Of(a A) Pair[A, B] {
return MakePair(a, o.m.Empty())
}
func (o *pairMonadHead[A, B, A1]) Map(f func(A) A1) func(Pair[A, B]) Pair[A1, B] {
return Map[B](f)
}
func (o *pairMonadHead[A, B, A1]) Chain(f func(A) Pair[A1, B]) func(Pair[A, B]) Pair[A1, B] {
return Chain[B, A, A1](o.s, f)
}
func (o *pairMonadHead[A, B, A1]) Ap(fa Pair[A, B]) func(Pair[func(A) A1, B]) Pair[A1, B] {
return Ap[B, A, A1](o.s, fa)
}
func (o *pairPointedHead[A, B]) Of(a A) Pair[A, B] {
return MakePair(a, o.m.Empty())
}
func (o *pairFunctorHead[A, B, A1]) Map(f func(A) A1) func(Pair[A, B]) Pair[A1, B] {
return Map[B, A, A1](f)
}
func (o *pairApplicativeHead[A, B, A1]) Map(f func(A) A1) func(Pair[A, B]) Pair[A1, B] {
return Map[B, A, A1](f)
}
func (o *pairApplicativeHead[A, B, A1]) Ap(fa Pair[A, B]) func(Pair[func(A) A1, B]) Pair[A1, B] {
return Ap[B, A, A1](o.s, fa)
}
func (o *pairApplicativeHead[A, B, A1]) Of(a A) Pair[A, B] {
return MakePair(a, o.m.Empty())
}
// Monad implements the monadic operations for [Pair]
func Monad[A, B, A1 any](m M.Monoid[B]) monad.Monad[A, A1, Pair[A, B], Pair[A1, B], Pair[func(A) A1, B]] {
return &pairMonadHead[A, B, A1]{s: M.ToSemigroup(m), m: m}
}
// Pointed implements the pointed operations for [Pair]
func Pointed[A, B any](m M.Monoid[B]) pointed.Pointed[A, Pair[A, B]] {
return &pairPointedHead[A, B]{m: m}
}
// Functor implements the functor operations for [Pair]
func Functor[A, B, A1 any]() functor.Functor[A, A1, Pair[A, B], Pair[A1, B]] {
return &pairFunctorHead[A, B, A1]{}
}
// Applicative implements the applicative operations for [Pair]
func Applicative[A, B, A1 any](m M.Monoid[B]) applicative.Applicative[A, A1, Pair[A, B], Pair[A1, B], Pair[func(A) A1, B]] {
return &pairApplicativeHead[A, B, A1]{s: M.ToSemigroup(m), m: m}
}
// MonadHead implements the monadic operations for [Pair]
func MonadHead[A, B, A1 any](m M.Monoid[B]) monad.Monad[A, A1, Pair[A, B], Pair[A1, B], Pair[func(A) A1, B]] {
return Monad[A, B, A1](m)
}
// PointedHead implements the pointed operations for [Pair]
func PointedHead[A, B any](m M.Monoid[B]) pointed.Pointed[A, Pair[A, B]] {
return PointedHead[A, B](m)
}
// FunctorHead implements the functor operations for [Pair]
func FunctorHead[A, B, A1 any]() functor.Functor[A, A1, Pair[A, B], Pair[A1, B]] {
return Functor[A, B, A1]()
}
// ApplicativeHead implements the applicative operations for [Pair]
func ApplicativeHead[A, B, A1 any](m M.Monoid[B]) applicative.Applicative[A, A1, Pair[A, B], Pair[A1, B], Pair[func(A) A1, B]] {
return Applicative[A, B, A1](m)
}
func (o *pairMonadTail[A, B, B1]) Of(b B) Pair[A, B] {
return MakePair(o.m.Empty(), b)
}
func (o *pairMonadTail[A, B, B1]) Map(f func(B) B1) func(Pair[A, B]) Pair[A, B1] {
return MapTail[A, B, B1](f)
}
func (o *pairMonadTail[A, B, B1]) Chain(f func(B) Pair[A, B1]) func(Pair[A, B]) Pair[A, B1] {
return ChainTail[A, B, B1](o.s, f)
}
func (o *pairMonadTail[A, B, B1]) Ap(fa Pair[A, B]) func(Pair[A, func(B) B1]) Pair[A, B1] {
return ApTail[A, B, B1](o.s, fa)
}
func (o *pairPointedTail[A, B]) Of(b B) Pair[A, B] {
return MakePair(o.m.Empty(), b)
}
func (o *pairFunctorTail[A, B, B1]) Map(f func(B) B1) func(Pair[A, B]) Pair[A, B1] {
return MapTail[A, B, B1](f)
}
func (o *pairApplicativeTail[A, B, B1]) Map(f func(B) B1) func(Pair[A, B]) Pair[A, B1] {
return MapTail[A, B, B1](f)
}
func (o *pairApplicativeTail[A, B, B1]) Ap(fa Pair[A, B]) func(Pair[A, func(B) B1]) Pair[A, B1] {
return ApTail[A, B, B1](o.s, fa)
}
func (o *pairApplicativeTail[A, B, B1]) Of(b B) Pair[A, B] {
return MakePair(o.m.Empty(), b)
}
// MonadTail implements the monadic operations for [Pair]
func MonadTail[B, A, B1 any](m M.Monoid[A]) monad.Monad[B, B1, Pair[A, B], Pair[A, B1], Pair[A, func(B) B1]] {
return &pairMonadTail[A, B, B1]{s: M.ToSemigroup(m), m: m}
}
// PointedTail implements the pointed operations for [Pair]
func PointedTail[B, A any](m M.Monoid[A]) pointed.Pointed[B, Pair[A, B]] {
return &pairPointedTail[A, B]{m: m}
}
// FunctorTail implements the functor operations for [Pair]
func FunctorTail[B, A, B1 any]() functor.Functor[B, B1, Pair[A, B], Pair[A, B1]] {
return &pairFunctorTail[A, B, B1]{}
}
// ApplicativeTail implements the applicative operations for [Pair]
func ApplicativeTail[B, A, B1 any](m M.Monoid[A]) applicative.Applicative[B, B1, Pair[A, B], Pair[A, B1], Pair[A, func(B) B1]] {
return &pairApplicativeTail[A, B, B1]{s: M.ToSemigroup(m), m: m}
}

204
pair/pair.go Normal file
View File

@@ -0,0 +1,204 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pair
import (
"fmt"
F "github.com/IBM/fp-go/function"
Sg "github.com/IBM/fp-go/semigroup"
T "github.com/IBM/fp-go/tuple"
)
type (
pair struct {
head, Tail any
}
// Pair defines a data structure that holds two strongly typed values
Pair[A, B any] pair
)
// String prints some debug info for the object
//
// go:noinline
func pairString(s *pair) string {
return fmt.Sprintf("Pair[%T, %t](%v, %v)", s.head, s.Tail, s.head, s.Tail)
}
// Format prints some debug info for the object
//
// go:noinline
func pairFormat(e *pair, f fmt.State, c rune) {
switch c {
case 's':
fmt.Fprint(f, pairString(e))
default:
fmt.Fprint(f, pairString(e))
}
}
// String prints some debug info for the object
func (s Pair[A, B]) String() string {
return pairString((*pair)(&s))
}
// Format prints some debug info for the object
func (s Pair[A, B]) Format(f fmt.State, c rune) {
pairFormat((*pair)(&s), f, c)
}
// Of creates a [Pair] with the same value to to both fields
func Of[A any](value A) Pair[A, A] {
return Pair[A, A]{head: value, Tail: value}
}
// FromTuple creates a [Pair] from a [T.Tuple2]
func FromTuple[A, B any](t T.Tuple2[A, B]) Pair[A, B] {
return Pair[A, B]{head: t.F1, Tail: t.F2}
}
// ToTuple creates a [T.Tuple2] from a [Pair]
func ToTuple[A, B any](t Pair[A, B]) T.Tuple2[A, B] {
return T.MakeTuple2(Head(t), Tail(t))
}
// MakePair creates a [Pair] from two values
func MakePair[A, B any](a A, b B) Pair[A, B] {
return Pair[A, B]{head: a, Tail: b}
}
// Head returns the head value of the pair
func Head[A, B any](fa Pair[A, B]) A {
return fa.head.(A)
}
// Tail returns the head value of the pair
func Tail[A, B any](fa Pair[A, B]) B {
return fa.Tail.(B)
}
// MonadMapHead maps the head value
func MonadMapHead[B, A, A1 any](fa Pair[A, B], f func(A) A1) Pair[A1, B] {
return Pair[A1, B]{f(Head(fa)), fa.Tail}
}
// MonadMap maps the head value
func MonadMap[B, A, A1 any](fa Pair[A, B], f func(A) A1) Pair[A1, B] {
return MonadMapHead(fa, f)
}
// MonadMapTail maps the Tail value
func MonadMapTail[A, B, B1 any](fa Pair[A, B], f func(B) B1) Pair[A, B1] {
return Pair[A, B1]{fa.head, f(Tail(fa))}
}
// MonadBiMap maps both values
func MonadBiMap[A, B, A1, B1 any](fa Pair[A, B], f func(A) A1, g func(B) B1) Pair[A1, B1] {
return Pair[A1, B1]{f(Head(fa)), g(Tail(fa))}
}
// Map maps the head value
func Map[B, A, A1 any](f func(A) A1) func(Pair[A, B]) Pair[A1, B] {
return MapHead[B, A, A1](f)
}
// MapHead maps the head value
func MapHead[B, A, A1 any](f func(A) A1) func(Pair[A, B]) Pair[A1, B] {
return F.Bind2nd(MonadMapHead[B, A, A1], f)
}
// MapTail maps the Tail value
func MapTail[A, B, B1 any](f func(B) B1) func(Pair[A, B]) Pair[A, B1] {
return F.Bind2nd(MonadMapTail[A, B, B1], f)
}
// BiMap maps both values
func BiMap[A, B, A1, B1 any](f func(A) A1, g func(B) B1) func(Pair[A, B]) Pair[A1, B1] {
return func(fa Pair[A, B]) Pair[A1, B1] {
return MonadBiMap(fa, f, g)
}
}
// MonadChainHead chains on the head value
func MonadChainHead[B, A, A1 any](sg Sg.Semigroup[B], fa Pair[A, B], f func(A) Pair[A1, B]) Pair[A1, B] {
fb := f(Head(fa))
return Pair[A1, B]{fb.head, sg.Concat(Tail(fa), Tail(fb))}
}
// MonadChainTail chains on the Tail value
func MonadChainTail[A, B, B1 any](sg Sg.Semigroup[A], fb Pair[A, B], f func(B) Pair[A, B1]) Pair[A, B1] {
fa := f(Tail(fb))
return Pair[A, B1]{sg.Concat(Head(fb), Head(fa)), fa.Tail}
}
// MonadChain chains on the head value
func MonadChain[B, A, A1 any](sg Sg.Semigroup[B], fa Pair[A, B], f func(A) Pair[A1, B]) Pair[A1, B] {
return MonadChainHead(sg, fa, f)
}
// ChainHead chains on the head value
func ChainHead[B, A, A1 any](sg Sg.Semigroup[B], f func(A) Pair[A1, B]) func(Pair[A, B]) Pair[A1, B] {
return func(fa Pair[A, B]) Pair[A1, B] {
return MonadChainHead(sg, fa, f)
}
}
// ChainTail chains on the Tail value
func ChainTail[A, B, B1 any](sg Sg.Semigroup[A], f func(B) Pair[A, B1]) func(Pair[A, B]) Pair[A, B1] {
return func(fa Pair[A, B]) Pair[A, B1] {
return MonadChainTail(sg, fa, f)
}
}
// Chain chains on the head value
func Chain[B, A, A1 any](sg Sg.Semigroup[B], f func(A) Pair[A1, B]) func(Pair[A, B]) Pair[A1, B] {
return ChainHead[B, A, A1](sg, f)
}
// MonadApHead applies on the head value
func MonadApHead[B, A, A1 any](sg Sg.Semigroup[B], faa Pair[func(A) A1, B], fa Pair[A, B]) Pair[A1, B] {
return Pair[A1, B]{Head(faa)(Head(fa)), sg.Concat(Tail(fa), Tail(faa))}
}
// MonadApTail applies on the Tail value
func MonadApTail[A, B, B1 any](sg Sg.Semigroup[A], fbb Pair[A, func(B) B1], fb Pair[A, B]) Pair[A, B1] {
return Pair[A, B1]{sg.Concat(Head(fb), Head(fbb)), Tail(fbb)(Tail(fb))}
}
// MonadAp applies on the head value
func MonadAp[B, A, A1 any](sg Sg.Semigroup[B], faa Pair[func(A) A1, B], fa Pair[A, B]) Pair[A1, B] {
return MonadApHead(sg, faa, fa)
}
// ApHead applies on the head value
func ApHead[B, A, A1 any](sg Sg.Semigroup[B], fa Pair[A, B]) func(Pair[func(A) A1, B]) Pair[A1, B] {
return func(faa Pair[func(A) A1, B]) Pair[A1, B] {
return MonadApHead(sg, faa, fa)
}
}
// ApTail applies on the Tail value
func ApTail[A, B, B1 any](sg Sg.Semigroup[A], fb Pair[A, B]) func(Pair[A, func(B) B1]) Pair[A, B1] {
return func(fbb Pair[A, func(B) B1]) Pair[A, B1] {
return MonadApTail(sg, fbb, fb)
}
}
// Ap applies on the head value
func Ap[B, A, A1 any](sg Sg.Semigroup[B], fa Pair[A, B]) func(Pair[func(A) A1, B]) Pair[A1, B] {
return ApHead[B, A, A1](sg, fa)
}

155
pair/testing/laws.go Normal file
View File

@@ -0,0 +1,155 @@
// Copyright (c) 2024 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
import (
"testing"
EQ "github.com/IBM/fp-go/eq"
L "github.com/IBM/fp-go/internal/monad/testing"
P "github.com/IBM/fp-go/pair"
M "github.com/IBM/fp-go/monoid"
)
// AssertLaws asserts the apply monad laws for the [P.Pair] monad
func assertLawsHead[E, A, B, C any](t *testing.T,
m M.Monoid[E],
eqe EQ.Eq[E],
eqa EQ.Eq[A],
eqb EQ.Eq[B],
eqc EQ.Eq[C],
ab func(A) B,
bc func(B) C,
) func(a A) bool {
fofc := P.Pointed[C](m)
fofaa := P.Pointed[func(A) A](m)
fofbc := P.Pointed[func(B) C](m)
fofabb := P.Pointed[func(func(A) B) B](m)
fmap := P.Functor[func(B) C, E, func(func(A) B) func(A) C]()
fapabb := P.Applicative[func(A) B, E, B](m)
fapabac := P.Applicative[func(A) B, E, func(A) C](m)
maa := P.Monad[A, E, A](m)
mab := P.Monad[A, E, B](m)
mac := P.Monad[A, E, C](m)
mbc := P.Monad[B, E, C](m)
return L.MonadAssertLaws(t,
P.Eq(eqa, eqe),
P.Eq(eqb, eqe),
P.Eq(eqc, eqe),
fofc,
fofaa,
fofbc,
fofabb,
fmap,
fapabb,
fapabac,
maa,
mab,
mac,
mbc,
ab,
bc,
)
}
// AssertLaws asserts the apply monad laws for the [P.Pair] monad
func assertLawsTail[E, A, B, C any](t *testing.T,
m M.Monoid[E],
eqe EQ.Eq[E],
eqa EQ.Eq[A],
eqb EQ.Eq[B],
eqc EQ.Eq[C],
ab func(A) B,
bc func(B) C,
) func(a A) bool {
fofc := P.PointedTail[C](m)
fofaa := P.PointedTail[func(A) A](m)
fofbc := P.PointedTail[func(B) C](m)
fofabb := P.PointedTail[func(func(A) B) B](m)
fmap := P.FunctorTail[func(B) C, E, func(func(A) B) func(A) C]()
fapabb := P.ApplicativeTail[func(A) B, E, B](m)
fapabac := P.ApplicativeTail[func(A) B, E, func(A) C](m)
maa := P.MonadTail[A, E, A](m)
mab := P.MonadTail[A, E, B](m)
mac := P.MonadTail[A, E, C](m)
mbc := P.MonadTail[B, E, C](m)
return L.MonadAssertLaws(t,
P.Eq(eqe, eqa),
P.Eq(eqe, eqb),
P.Eq(eqe, eqc),
fofc,
fofaa,
fofbc,
fofabb,
fmap,
fapabb,
fapabac,
maa,
mab,
mac,
mbc,
ab,
bc,
)
}
// AssertLaws asserts the apply monad laws for the [P.Pair] monad
func AssertLaws[E, A, B, C any](t *testing.T,
m M.Monoid[E],
eqe EQ.Eq[E],
eqa EQ.Eq[A],
eqb EQ.Eq[B],
eqc EQ.Eq[C],
ab func(A) B,
bc func(B) C,
) func(A) bool {
head := assertLawsHead(t, m, eqe, eqa, eqb, eqc, ab, bc)
tail := assertLawsHead(t, m, eqe, eqa, eqb, eqc, ab, bc)
return func(a A) bool {
return head(a) && tail(a)
}
}

51
pair/testing/laws_test.go Normal file
View File

@@ -0,0 +1,51 @@
// Copyright (c) 2023 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
import (
"fmt"
"testing"
EQ "github.com/IBM/fp-go/eq"
S "github.com/IBM/fp-go/string"
"github.com/stretchr/testify/assert"
)
func TestMonadLaws(t *testing.T) {
// some comparison
eqe := EQ.FromStrictEquals[string]()
eqa := EQ.FromStrictEquals[bool]()
eqb := EQ.FromStrictEquals[int]()
eqc := EQ.FromStrictEquals[string]()
m := S.Monoid
ab := func(a bool) int {
if a {
return 1
}
return 0
}
bc := func(b int) string {
return fmt.Sprintf("value %d", b)
}
laws := AssertLaws(t, m, eqe, eqa, eqb, eqc, ab, bc)
assert.True(t, laws(true))
assert.True(t, laws(false))
}