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

@@ -109,17 +109,21 @@ func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
return ReadJSON[A](client)
}
// ReadJSON sends a request, reads the response and parses the response as JSON
func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
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],
)),
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

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

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

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],
@@ -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
//
// 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],
@@ -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
//
// 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],
@@ -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'
//
// 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],
@@ -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)
}
}
// 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 (
"github.com/IBM/fp-go/internal/apply"
"github.com/IBM/fp-go/internal/functor"
"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]
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],
@@ -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`
//
// 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],
@@ -98,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)
}
}

View File

@@ -23,3 +23,8 @@ 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

@@ -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/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],
@@ -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`
//
// 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],
@@ -95,3 +134,37 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
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 (
"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

@@ -20,12 +20,15 @@ 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 {
@@ -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
//
// 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,
@@ -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`
//
// Deprecated: use [FunctorAssertLaws] instead
func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
eqa E.Eq[HKTA],
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,
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 {
@@ -73,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)
}
}

View File

@@ -17,10 +17,38 @@ 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],
@@ -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

@@ -124,17 +124,22 @@ 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 JSON
func ReadJSON[A any](client Client) func(Requester) IOE.IOEither[error, A] {
// 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],
)),
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

@@ -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 creator(
F.Flow2(get, fromPred),
func(s S, a A) S {
func(s S, _ A) S {
return F.Pipe3(
s,
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))
}