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:
@@ -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
|
||||
|
@@ -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]),
|
||||
)
|
||||
}
|
||||
|
@@ -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]),
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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]),
|
||||
)
|
||||
}
|
||||
|
@@ -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
33
pair/eq.go
Normal 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
193
pair/monad.go
Normal 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
204
pair/pair.go
Normal 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
155
pair/testing/laws.go
Normal 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
51
pair/testing/laws_test.go
Normal 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))
|
||||
}
|
Reference in New Issue
Block a user