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] {
|
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||||
return func(w W) RIOE.ReaderIOEither[[]byte] {
|
return func(w W) RIOE.ReaderIOEither[[]byte] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
RIOE.TryCatch(func(ctx context.Context) func() ([]byte, error) {
|
RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) {
|
||||||
return func() ([]byte, error) {
|
return func() ([]byte, error) {
|
||||||
_, err := w.Write(data)
|
_, err := w.Write(data)
|
||||||
return data, err
|
return data, err
|
||||||
|
@@ -109,17 +109,21 @@ func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
|||||||
return ReadJSON[A](client)
|
return ReadJSON[A](client)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadJSON sends a request, reads the response and parses the response as JSON
|
func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
||||||
func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
|
||||||
return F.Flow3(
|
return F.Flow3(
|
||||||
ReadFullResponse(client),
|
ReadFullResponse(client),
|
||||||
RIOE.ChainFirstEitherK(F.Flow2(
|
RIOE.ChainFirstEitherK(F.Flow2(
|
||||||
H.Response,
|
H.Response,
|
||||||
H.ValidateJSONResponse,
|
H.ValidateJSONResponse,
|
||||||
)),
|
)),
|
||||||
RIOE.ChainEitherK(F.Flow2(
|
RIOE.Map(H.Body),
|
||||||
H.Body,
|
)
|
||||||
J.Unmarshal[A],
|
}
|
||||||
)),
|
|
||||||
|
// ReadJSON sends a request, reads the response and parses the response as JSON
|
||||||
|
func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
||||||
|
return F.Flow2(
|
||||||
|
readJSON(client),
|
||||||
|
RIOE.ChainEitherK(J.Unmarshal[A]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,7 @@ func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
||||||
return func(params ...any) IOE.IOEither[error, any] {
|
return func(_ ...any) IOE.IOEither[error, any] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
f,
|
f,
|
||||||
IOE.Map[error](F.ToAny[R]),
|
IOE.Map[error](F.ToAny[R]),
|
||||||
|
@@ -36,7 +36,7 @@ func Map[A, B any](f func(A) B) func(A) B {
|
|||||||
return G.Map(f)
|
return G.Map(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadMapTo[A, B any](fa A, b B) B {
|
func MonadMapTo[A, B any](_ A, b B) B {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,13 +20,19 @@ import (
|
|||||||
|
|
||||||
E "github.com/IBM/fp-go/eq"
|
E "github.com/IBM/fp-go/eq"
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/applicative"
|
||||||
|
"github.com/IBM/fp-go/internal/apply"
|
||||||
L "github.com/IBM/fp-go/internal/apply/testing"
|
L "github.com/IBM/fp-go/internal/apply/testing"
|
||||||
|
"github.com/IBM/fp-go/internal/functor"
|
||||||
|
"github.com/IBM/fp-go/internal/pointed"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Applicative identity law
|
// Applicative identity law
|
||||||
//
|
//
|
||||||
// A.ap(A.of(a => a), fa) <-> fa
|
// A.ap(A.of(a => a), fa) <-> fa
|
||||||
|
//
|
||||||
|
// Deprecated: use [ApplicativeAssertIdentity]
|
||||||
func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
|
func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
|
||||||
eq E.Eq[HKTA],
|
eq E.Eq[HKTA],
|
||||||
|
|
||||||
@@ -46,9 +52,33 @@ func AssertIdentity[HKTA, HKTAA, A any](t *testing.T,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Applicative identity law
|
||||||
|
//
|
||||||
|
// A.ap(A.of(a => a), fa) <-> fa
|
||||||
|
func ApplicativeAssertIdentity[HKTA, HKTFAA, A any](t *testing.T,
|
||||||
|
eq E.Eq[HKTA],
|
||||||
|
|
||||||
|
ap applicative.Applicative[A, A, HKTA, HKTA, HKTFAA],
|
||||||
|
paa pointed.Pointed[func(A) A, HKTFAA],
|
||||||
|
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
// mark as test helper
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
|
||||||
|
left := ap.Ap(fa)(paa.Of(F.Identity[A]))
|
||||||
|
right := fa
|
||||||
|
|
||||||
|
return assert.True(t, eq.Equals(left, right), "Applicative identity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Applicative homomorphism law
|
// Applicative homomorphism law
|
||||||
//
|
//
|
||||||
// A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a))
|
// A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a))
|
||||||
|
//
|
||||||
|
// Deprecated: use [ApplicativeAssertHomomorphism]
|
||||||
func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
|
func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
|
||||||
eq E.Eq[HKTB],
|
eq E.Eq[HKTB],
|
||||||
|
|
||||||
@@ -72,9 +102,35 @@ func AssertHomomorphism[HKTA, HKTB, HKTAB, A, B any](t *testing.T,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Applicative homomorphism law
|
||||||
|
//
|
||||||
|
// A.ap(A.of(ab), A.of(a)) <-> A.of(ab(a))
|
||||||
|
func ApplicativeAssertHomomorphism[HKTA, HKTB, HKTFAB, A, B any](t *testing.T,
|
||||||
|
eq E.Eq[HKTB],
|
||||||
|
|
||||||
|
apab applicative.Applicative[A, B, HKTA, HKTB, HKTFAB],
|
||||||
|
pb pointed.Pointed[B, HKTB],
|
||||||
|
pfab pointed.Pointed[func(A) B, HKTFAB],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
) func(a A) bool {
|
||||||
|
// mark as test helper
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
return func(a A) bool {
|
||||||
|
|
||||||
|
left := apab.Ap(apab.Of(a))(pfab.Of(ab))
|
||||||
|
right := pb.Of(ab(a))
|
||||||
|
|
||||||
|
return assert.True(t, eq.Equals(left, right), "Applicative homomorphism")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Applicative interchange law
|
// Applicative interchange law
|
||||||
//
|
//
|
||||||
// A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab)
|
// A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab)
|
||||||
|
//
|
||||||
|
// Deprecated: use [ApplicativeAssertInterchange]
|
||||||
func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
|
func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
|
||||||
eq E.Eq[HKTB],
|
eq E.Eq[HKTB],
|
||||||
|
|
||||||
@@ -103,7 +159,38 @@ func AssertInterchange[HKTA, HKTB, HKTAB, HKTABB, A, B any](t *testing.T,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Applicative interchange law
|
||||||
|
//
|
||||||
|
// A.ap(fab, A.of(a)) <-> A.ap(A.of(ab => ab(a)), fab)
|
||||||
|
func ApplicativeAssertInterchange[HKTA, HKTB, HKTFAB, HKTABB, A, B any](t *testing.T,
|
||||||
|
eq E.Eq[HKTB],
|
||||||
|
|
||||||
|
apab applicative.Applicative[A, B, HKTA, HKTB, HKTFAB],
|
||||||
|
apabb applicative.Applicative[func(A) B, B, HKTFAB, HKTB, HKTABB],
|
||||||
|
pabb pointed.Pointed[func(func(A) B) B, HKTABB],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
) func(a A) bool {
|
||||||
|
// mark as test helper
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
return func(a A) bool {
|
||||||
|
|
||||||
|
fab := apabb.Of(ab)
|
||||||
|
|
||||||
|
left := apab.Ap(apab.Of(a))(fab)
|
||||||
|
|
||||||
|
right := apabb.Ap(fab)(pabb.Of(func(ab func(A) B) B {
|
||||||
|
return ab(a)
|
||||||
|
}))
|
||||||
|
|
||||||
|
return assert.True(t, eq.Equals(left, right), "Applicative homomorphism")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange'
|
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange'
|
||||||
|
//
|
||||||
|
// Deprecated: use [ApplicativeAssertLaws] instead
|
||||||
func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
|
func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
|
||||||
eqa E.Eq[HKTA],
|
eqa E.Eq[HKTA],
|
||||||
eqb E.Eq[HKTB],
|
eqb E.Eq[HKTB],
|
||||||
@@ -150,3 +237,47 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
|
|||||||
return apply(fa) && identity(fa) && homomorphism(a) && interchange(a)
|
return apply(fa) && identity(fa) && homomorphism(a) && interchange(a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplicativeAssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange'
|
||||||
|
func ApplicativeAssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
|
||||||
|
eqa E.Eq[HKTA],
|
||||||
|
eqb E.Eq[HKTB],
|
||||||
|
eqc E.Eq[HKTC],
|
||||||
|
|
||||||
|
fofb pointed.Pointed[B, HKTB],
|
||||||
|
|
||||||
|
fofaa pointed.Pointed[func(A) A, HKTAA],
|
||||||
|
fofbc pointed.Pointed[func(B) C, HKTBC],
|
||||||
|
|
||||||
|
fofabb pointed.Pointed[func(func(A) B) B, HKTABB],
|
||||||
|
|
||||||
|
faa functor.Functor[A, A, HKTA, HKTA],
|
||||||
|
|
||||||
|
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
|
||||||
|
|
||||||
|
fapaa applicative.Applicative[A, A, HKTA, HKTA, HKTAA],
|
||||||
|
fapab applicative.Applicative[A, B, HKTA, HKTB, HKTAB],
|
||||||
|
fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC],
|
||||||
|
fapac apply.Apply[A, C, HKTA, HKTC, HKTAC],
|
||||||
|
|
||||||
|
fapabb applicative.Applicative[func(A) B, B, HKTAB, HKTB, HKTABB],
|
||||||
|
fapabac applicative.Applicative[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(a A) bool {
|
||||||
|
// mark as test helper
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// apply laws
|
||||||
|
apply := L.ApplyAssertLaws(t, eqa, eqc, applicative.ToPointed(fapabac), fofbc, faa, fmap, applicative.ToApply(fapab), fapbc, fapac, applicative.ToApply(fapabac), ab, bc)
|
||||||
|
// applicative laws
|
||||||
|
identity := ApplicativeAssertIdentity(t, eqa, fapaa, fofaa)
|
||||||
|
homomorphism := ApplicativeAssertHomomorphism(t, eqb, fapab, fofb, applicative.ToPointed(fapabb), ab)
|
||||||
|
interchange := ApplicativeAssertInterchange(t, eqb, fapab, fapabb, fofabb, ab)
|
||||||
|
|
||||||
|
return func(a A) bool {
|
||||||
|
fa := fapaa.Of(a)
|
||||||
|
return apply(fa) && identity(fa) && homomorphism(a) && interchange(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -17,6 +17,7 @@ package applicative
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/internal/apply"
|
"github.com/IBM/fp-go/internal/apply"
|
||||||
|
"github.com/IBM/fp-go/internal/functor"
|
||||||
"github.com/IBM/fp-go/internal/pointed"
|
"github.com/IBM/fp-go/internal/pointed"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,3 +25,18 @@ type Applicative[A, B, HKTA, HKTB, HKTFAB any] interface {
|
|||||||
apply.Apply[A, B, HKTA, HKTB, HKTFAB]
|
apply.Apply[A, B, HKTA, HKTB, HKTFAB]
|
||||||
pointed.Pointed[A, HKTA]
|
pointed.Pointed[A, HKTA]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToFunctor converts from [Applicative] to [functor.Functor]
|
||||||
|
func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToApply converts from [Applicative] to [apply.Apply]
|
||||||
|
func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPointed converts from [Applicative] to [pointed.Pointed]
|
||||||
|
func ToPointed[A, B, HKTA, HKTB, HKTFAB any](ap Applicative[A, B, HKTA, HKTB, HKTFAB]) pointed.Pointed[A, HKTA] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
@@ -19,13 +19,18 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
E "github.com/IBM/fp-go/eq"
|
E "github.com/IBM/fp-go/eq"
|
||||||
|
"github.com/IBM/fp-go/internal/apply"
|
||||||
|
"github.com/IBM/fp-go/internal/functor"
|
||||||
FCT "github.com/IBM/fp-go/internal/functor/testing"
|
FCT "github.com/IBM/fp-go/internal/functor/testing"
|
||||||
|
"github.com/IBM/fp-go/internal/pointed"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Apply associative composition law
|
// Apply associative composition law
|
||||||
//
|
//
|
||||||
// F.ap(F.ap(F.map(fbc, bc => ab => a => bc(ab(a))), fab), fa) <-> F.ap(fbc, F.ap(fab, fa))
|
// F.ap(F.ap(F.map(fbc, bc => ab => a => bc(ab(a))), fab), fa) <-> F.ap(fbc, F.ap(fab, fa))
|
||||||
|
//
|
||||||
|
// Deprecated: use [ApplyAssertAssociativeComposition] instead
|
||||||
func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
||||||
eq E.Eq[HKTC],
|
eq E.Eq[HKTC],
|
||||||
|
|
||||||
@@ -63,7 +68,49 @@ func AssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply associative composition law
|
||||||
|
//
|
||||||
|
// F.ap(F.ap(F.map(fbc, bc => ab => a => bc(ab(a))), fab), fa) <-> F.ap(fbc, F.ap(fab, fa))
|
||||||
|
func ApplyAssertAssociativeComposition[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
||||||
|
eq E.Eq[HKTC],
|
||||||
|
|
||||||
|
fofab pointed.Pointed[func(A) B, HKTAB],
|
||||||
|
fofbc pointed.Pointed[func(B) C, HKTBC],
|
||||||
|
|
||||||
|
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
|
||||||
|
|
||||||
|
fapab apply.Apply[A, B, HKTA, HKTB, HKTAB],
|
||||||
|
fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC],
|
||||||
|
fapac apply.Apply[A, C, HKTA, HKTC, HKTAC],
|
||||||
|
|
||||||
|
fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
t.Helper()
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
|
||||||
|
fab := fofab.Of(ab)
|
||||||
|
fbc := fofbc.Of(bc)
|
||||||
|
|
||||||
|
left := fapac.Ap(fa)(fapabac.Ap(fab)(fmap.Map(func(bc func(B) C) func(func(A) B) func(A) C {
|
||||||
|
return func(ab func(A) B) func(A) C {
|
||||||
|
return func(a A) C {
|
||||||
|
return bc(ab(a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(fbc)))
|
||||||
|
|
||||||
|
right := fapbc.Ap(fapab.Ap(fa)(fab))(fbc)
|
||||||
|
|
||||||
|
return assert.True(t, eq.Equals(left, right), "Apply associative composition")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AssertLaws asserts the apply laws `identity`, `composition` and `associative composition`
|
// AssertLaws asserts the apply laws `identity`, `composition` and `associative composition`
|
||||||
|
//
|
||||||
|
// Deprecated: use [ApplyAssertLaws] instead
|
||||||
func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
||||||
eqa E.Eq[HKTA],
|
eqa E.Eq[HKTA],
|
||||||
eqc E.Eq[HKTC],
|
eqc E.Eq[HKTC],
|
||||||
@@ -98,3 +145,36 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
|
|||||||
return functor(fa) && composition(fa)
|
return functor(fa) && composition(fa)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyAssertLaws asserts the apply laws `identity`, `composition` and `associative composition`
|
||||||
|
func ApplyAssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
||||||
|
eqa E.Eq[HKTA],
|
||||||
|
eqc E.Eq[HKTC],
|
||||||
|
|
||||||
|
fofab pointed.Pointed[func(A) B, HKTAB],
|
||||||
|
fofbc pointed.Pointed[func(B) C, HKTBC],
|
||||||
|
|
||||||
|
faa functor.Functor[A, A, HKTA, HKTA],
|
||||||
|
|
||||||
|
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
|
||||||
|
|
||||||
|
fapab apply.Apply[A, B, HKTA, HKTB, HKTAB],
|
||||||
|
fapbc apply.Apply[B, C, HKTB, HKTC, HKTBC],
|
||||||
|
fapac apply.Apply[A, C, HKTA, HKTC, HKTAC],
|
||||||
|
|
||||||
|
fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
// mark as test helper
|
||||||
|
t.Helper()
|
||||||
|
// functor laws
|
||||||
|
functor := FCT.FunctorAssertLaws(t, eqa, eqc, faa, apply.ToFunctor(fapab), apply.ToFunctor(fapac), apply.ToFunctor(fapbc), ab, bc)
|
||||||
|
// associative composition laws
|
||||||
|
composition := ApplyAssertAssociativeComposition(t, eqc, fofab, fofbc, fmap, fapab, fapbc, fapac, fapabac, ab, bc)
|
||||||
|
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
return functor(fa) && composition(fa)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -23,3 +23,8 @@ type Apply[A, B, HKTA, HKTB, HKTFAB any] interface {
|
|||||||
functor.Functor[A, B, HKTA, HKTB]
|
functor.Functor[A, B, HKTA, HKTB]
|
||||||
Ap(HKTA) func(HKTFAB) HKTB
|
Ap(HKTA) func(HKTFAB) HKTB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToFunctor converts from [Apply] to [functor.Functor]
|
||||||
|
func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Apply[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
@@ -20,13 +20,19 @@ import (
|
|||||||
|
|
||||||
E "github.com/IBM/fp-go/eq"
|
E "github.com/IBM/fp-go/eq"
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/apply"
|
||||||
L "github.com/IBM/fp-go/internal/apply/testing"
|
L "github.com/IBM/fp-go/internal/apply/testing"
|
||||||
|
"github.com/IBM/fp-go/internal/chain"
|
||||||
|
"github.com/IBM/fp-go/internal/functor"
|
||||||
|
"github.com/IBM/fp-go/internal/pointed"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Chain associativity law
|
// Chain associativity law
|
||||||
//
|
//
|
||||||
// F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc))
|
// F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc))
|
||||||
|
//
|
||||||
|
// Deprecated: use [ChainAssertAssociativity] instead
|
||||||
func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
|
func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
|
||||||
eq E.Eq[HKTC],
|
eq E.Eq[HKTC],
|
||||||
|
|
||||||
@@ -55,7 +61,40 @@ func AssertAssociativity[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chain associativity law
|
||||||
|
//
|
||||||
|
// F.chain(F.chain(fa, afb), bfc) <-> F.chain(fa, a => F.chain(afb(a), bfc))
|
||||||
|
func ChainAssertAssociativity[HKTA, HKTB, HKTC, HKTAB, HKTAC, HKTBC, A, B, C any](t *testing.T,
|
||||||
|
eq E.Eq[HKTC],
|
||||||
|
|
||||||
|
fofb pointed.Pointed[B, HKTB],
|
||||||
|
fofc pointed.Pointed[C, HKTC],
|
||||||
|
|
||||||
|
chainab chain.Chainable[A, B, HKTA, HKTB, HKTAB],
|
||||||
|
chainac chain.Chainable[A, C, HKTA, HKTC, HKTAC],
|
||||||
|
chainbc chain.Chainable[B, C, HKTB, HKTC, HKTBC],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
|
||||||
|
afb := F.Flow2(ab, fofb.Of)
|
||||||
|
bfc := F.Flow2(bc, fofc.Of)
|
||||||
|
|
||||||
|
left := chainbc.Chain(bfc)(chainab.Chain(afb)(fa))
|
||||||
|
|
||||||
|
right := chainac.Chain(func(a A) HKTC {
|
||||||
|
return chainbc.Chain(bfc)(afb(a))
|
||||||
|
})(fa)
|
||||||
|
|
||||||
|
return assert.True(t, eq.Equals(left, right), "Chain associativity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition` and `associativity`
|
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition` and `associativity`
|
||||||
|
//
|
||||||
|
// Deprecated: use [ChainAssertLaws] instead
|
||||||
func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
||||||
eqa E.Eq[HKTA],
|
eqa E.Eq[HKTA],
|
||||||
eqc E.Eq[HKTC],
|
eqc E.Eq[HKTC],
|
||||||
@@ -95,3 +134,37 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *
|
|||||||
return apply(fa) && associativity(fa)
|
return apply(fa) && associativity(fa)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChainAssertLaws asserts the apply laws `identity`, `composition`, `associative composition` and `associativity`
|
||||||
|
func ChainAssertLaws[HKTA, HKTB, HKTC, HKTAB, HKTBC, HKTAC, HKTABAC, A, B, C any](t *testing.T,
|
||||||
|
eqa E.Eq[HKTA],
|
||||||
|
eqc E.Eq[HKTC],
|
||||||
|
|
||||||
|
fofb pointed.Pointed[B, HKTB],
|
||||||
|
fofc pointed.Pointed[C, HKTC],
|
||||||
|
|
||||||
|
fofab pointed.Pointed[func(A) B, HKTAB],
|
||||||
|
fofbc pointed.Pointed[func(B) C, HKTBC],
|
||||||
|
|
||||||
|
faa functor.Functor[A, A, HKTA, HKTA],
|
||||||
|
|
||||||
|
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
|
||||||
|
|
||||||
|
chainab chain.Chainable[A, B, HKTA, HKTB, HKTAB],
|
||||||
|
chainac chain.Chainable[A, C, HKTA, HKTC, HKTAC],
|
||||||
|
chainbc chain.Chainable[B, C, HKTB, HKTC, HKTBC],
|
||||||
|
|
||||||
|
fapabac apply.Apply[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
// apply laws
|
||||||
|
apply := L.ApplyAssertLaws(t, eqa, eqc, fofab, fofbc, faa, fmap, chain.ToApply(chainab), chain.ToApply(chainbc), chain.ToApply(chainac), fapabac, ab, bc)
|
||||||
|
// chain laws
|
||||||
|
associativity := ChainAssertAssociativity(t, eqc, fofb, fofc, chainab, chainac, chainbc, ab, bc)
|
||||||
|
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
return apply(fa) && associativity(fa)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -17,9 +17,20 @@ package chain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/internal/apply"
|
"github.com/IBM/fp-go/internal/apply"
|
||||||
|
"github.com/IBM/fp-go/internal/functor"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Chainable[A, B, HKTA, HKTB, HKTFAB any] interface {
|
type Chainable[A, B, HKTA, HKTB, HKTFAB any] interface {
|
||||||
apply.Apply[A, B, HKTA, HKTB, HKTFAB]
|
apply.Apply[A, B, HKTA, HKTB, HKTFAB]
|
||||||
Chain(func(A) HKTB) func(HKTA) HKTB
|
Chain(func(A) HKTB) func(HKTA) HKTB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToFunctor converts from [Chainable] to [functor.Functor]
|
||||||
|
func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Chainable[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToApply converts from [Chainable] to [functor.Functor]
|
||||||
|
func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Chainable[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
@@ -20,12 +20,15 @@ import (
|
|||||||
|
|
||||||
E "github.com/IBM/fp-go/eq"
|
E "github.com/IBM/fp-go/eq"
|
||||||
F "github.com/IBM/fp-go/function"
|
F "github.com/IBM/fp-go/function"
|
||||||
|
"github.com/IBM/fp-go/internal/functor"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Functor identity law
|
// Functor identity law
|
||||||
//
|
//
|
||||||
// F.map(fa, a => a) <-> fa
|
// F.map(fa, a => a) <-> fa
|
||||||
|
//
|
||||||
|
// Deprecated: use [FunctorAssertIdentity]
|
||||||
func AssertIdentity[HKTA, A any](t *testing.T, eq E.Eq[HKTA], fmap func(HKTA, func(A) A) HKTA) func(fa HKTA) bool {
|
func AssertIdentity[HKTA, A any](t *testing.T, eq E.Eq[HKTA], fmap func(HKTA, func(A) A) HKTA) func(fa HKTA) bool {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return func(fa HKTA) bool {
|
return func(fa HKTA) bool {
|
||||||
@@ -33,9 +36,28 @@ func AssertIdentity[HKTA, A any](t *testing.T, eq E.Eq[HKTA], fmap func(HKTA, fu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functor identity law
|
||||||
|
//
|
||||||
|
// F.map(fa, a => a) <-> fa
|
||||||
|
func FunctorAssertIdentity[HKTA, A any](
|
||||||
|
t *testing.T,
|
||||||
|
eq E.Eq[HKTA],
|
||||||
|
|
||||||
|
fca functor.Functor[A, A, HKTA, HKTA],
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
|
||||||
|
t.Helper()
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
|
||||||
|
return assert.True(t, eq.Equals(fa, fca.Map(F.Identity[A])(fa)), "Functor identity law")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Functor composition law
|
// Functor composition law
|
||||||
//
|
//
|
||||||
// F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc)
|
// F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc)
|
||||||
|
//
|
||||||
|
// Deprecated: use [FunctorAssertComposition] instead
|
||||||
func AssertComposition[HKTA, HKTB, HKTC, A, B, C any](
|
func AssertComposition[HKTA, HKTB, HKTC, A, B, C any](
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
|
|
||||||
@@ -53,7 +75,30 @@ func AssertComposition[HKTA, HKTB, HKTC, A, B, C any](
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functor composition law
|
||||||
|
//
|
||||||
|
// F.map(fa, a => bc(ab(a))) <-> F.map(F.map(fa, ab), bc)
|
||||||
|
func FunctorAssertComposition[HKTA, HKTB, HKTC, A, B, C any](
|
||||||
|
t *testing.T,
|
||||||
|
|
||||||
|
eq E.Eq[HKTC],
|
||||||
|
|
||||||
|
fab functor.Functor[A, B, HKTA, HKTB],
|
||||||
|
fac functor.Functor[A, C, HKTA, HKTC],
|
||||||
|
fbc functor.Functor[B, C, HKTB, HKTC],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
t.Helper()
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
return assert.True(t, eq.Equals(fac.Map(F.Flow2(ab, bc))(fa), fbc.Map(bc)(fab.Map(ab)(fa))), "Functor composition law")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AssertLaws asserts the functor laws `identity` and `composition`
|
// AssertLaws asserts the functor laws `identity` and `composition`
|
||||||
|
//
|
||||||
|
// Deprecated: use [FunctorAssertLaws] instead
|
||||||
func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
|
func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
|
||||||
eqa E.Eq[HKTA],
|
eqa E.Eq[HKTA],
|
||||||
eqc E.Eq[HKTC],
|
eqc E.Eq[HKTC],
|
||||||
@@ -62,6 +107,7 @@ func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
|
|||||||
fab func(HKTA, func(A) B) HKTB,
|
fab func(HKTA, func(A) B) HKTB,
|
||||||
fac func(HKTA, func(A) C) HKTC,
|
fac func(HKTA, func(A) C) HKTC,
|
||||||
fbc func(HKTB, func(B) C) HKTC,
|
fbc func(HKTB, func(B) C) HKTC,
|
||||||
|
|
||||||
ab func(A) B,
|
ab func(A) B,
|
||||||
bc func(B) C,
|
bc func(B) C,
|
||||||
) func(fa HKTA) bool {
|
) func(fa HKTA) bool {
|
||||||
@@ -73,3 +119,25 @@ func AssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
|
|||||||
return identity(fa) && composition(fa)
|
return identity(fa) && composition(fa)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FunctorAssertLaws asserts the functor laws `identity` and `composition`
|
||||||
|
func FunctorAssertLaws[HKTA, HKTB, HKTC, A, B, C any](t *testing.T,
|
||||||
|
eqa E.Eq[HKTA],
|
||||||
|
eqc E.Eq[HKTC],
|
||||||
|
|
||||||
|
faa functor.Functor[A, A, HKTA, HKTA],
|
||||||
|
fab functor.Functor[A, B, HKTA, HKTB],
|
||||||
|
fac functor.Functor[A, C, HKTA, HKTC],
|
||||||
|
fbc functor.Functor[B, C, HKTB, HKTC],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
t.Helper()
|
||||||
|
identity := FunctorAssertIdentity(t, eqa, faa)
|
||||||
|
composition := FunctorAssertComposition(t, eqc, fab, fac, fbc, ab, bc)
|
||||||
|
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
return identity(fa) && composition(fa)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -17,10 +17,38 @@ package monad
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/internal/applicative"
|
"github.com/IBM/fp-go/internal/applicative"
|
||||||
|
"github.com/IBM/fp-go/internal/apply"
|
||||||
"github.com/IBM/fp-go/internal/chain"
|
"github.com/IBM/fp-go/internal/chain"
|
||||||
|
"github.com/IBM/fp-go/internal/functor"
|
||||||
|
"github.com/IBM/fp-go/internal/pointed"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Monad[A, B, HKTA, HKTB, HKTFAB any] interface {
|
type Monad[A, B, HKTA, HKTB, HKTFAB any] interface {
|
||||||
applicative.Applicative[A, B, HKTA, HKTB, HKTFAB]
|
applicative.Applicative[A, B, HKTA, HKTB, HKTFAB]
|
||||||
chain.Chainable[A, B, HKTA, HKTB, HKTFAB]
|
chain.Chainable[A, B, HKTA, HKTB, HKTFAB]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToFunctor converts from [Monad] to [functor.Functor]
|
||||||
|
func ToFunctor[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) functor.Functor[A, B, HKTA, HKTB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToApply converts from [Monad] to [apply.Apply]
|
||||||
|
func ToApply[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) apply.Apply[A, B, HKTA, HKTB, HKTFAB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPointed converts from [Monad] to [pointed.Pointed]
|
||||||
|
func ToPointed[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) pointed.Pointed[A, HKTA] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToApplicative converts from [Monad] to [applicative.Applicative]
|
||||||
|
func ToApplicative[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) applicative.Applicative[A, B, HKTA, HKTB, HKTFAB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToChainable converts from [Monad] to [chain.Chainable]
|
||||||
|
func ToChainable[A, B, HKTA, HKTB, HKTFAB any](ap Monad[A, B, HKTA, HKTB, HKTFAB]) chain.Chainable[A, B, HKTA, HKTB, HKTFAB] {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
@@ -19,14 +19,21 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
E "github.com/IBM/fp-go/eq"
|
E "github.com/IBM/fp-go/eq"
|
||||||
|
"github.com/IBM/fp-go/internal/applicative"
|
||||||
LA "github.com/IBM/fp-go/internal/applicative/testing"
|
LA "github.com/IBM/fp-go/internal/applicative/testing"
|
||||||
|
"github.com/IBM/fp-go/internal/chain"
|
||||||
LC "github.com/IBM/fp-go/internal/chain/testing"
|
LC "github.com/IBM/fp-go/internal/chain/testing"
|
||||||
|
"github.com/IBM/fp-go/internal/functor"
|
||||||
|
"github.com/IBM/fp-go/internal/monad"
|
||||||
|
"github.com/IBM/fp-go/internal/pointed"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Apply monad left identity law
|
// Apply monad left identity law
|
||||||
//
|
//
|
||||||
// M.chain(M.of(a), f) <-> f(a)
|
// M.chain(M.of(a), f) <-> f(a)
|
||||||
|
//
|
||||||
|
// Deprecated: use [MonadAssertLeftIdentity] instead
|
||||||
func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T,
|
func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T,
|
||||||
eq E.Eq[HKTB],
|
eq E.Eq[HKTB],
|
||||||
|
|
||||||
@@ -50,9 +57,36 @@ func AssertLeftIdentity[HKTA, HKTB, A, B any](t *testing.T,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply monad left identity law
|
||||||
|
//
|
||||||
|
// M.chain(M.of(a), f) <-> f(a)
|
||||||
|
func MonadAssertLeftIdentity[HKTA, HKTB, HKTFAB, A, B any](t *testing.T,
|
||||||
|
eq E.Eq[HKTB],
|
||||||
|
|
||||||
|
fofb pointed.Pointed[B, HKTB],
|
||||||
|
|
||||||
|
ma monad.Monad[A, B, HKTA, HKTB, HKTFAB],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
) func(a A) bool {
|
||||||
|
return func(a A) bool {
|
||||||
|
|
||||||
|
f := func(a A) HKTB {
|
||||||
|
return fofb.Of(ab(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
left := ma.Chain(f)(ma.Of(a))
|
||||||
|
right := f(a)
|
||||||
|
|
||||||
|
return assert.True(t, eq.Equals(left, right), "Monad left identity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply monad right identity law
|
// Apply monad right identity law
|
||||||
//
|
//
|
||||||
// M.chain(fa, M.of) <-> fa
|
// M.chain(fa, M.of) <-> fa
|
||||||
|
//
|
||||||
|
// Deprecated: use [MonadAssertRightIdentity] instead
|
||||||
func AssertRightIdentity[HKTA, A any](t *testing.T,
|
func AssertRightIdentity[HKTA, A any](t *testing.T,
|
||||||
eq E.Eq[HKTA],
|
eq E.Eq[HKTA],
|
||||||
|
|
||||||
@@ -69,7 +103,27 @@ func AssertRightIdentity[HKTA, A any](t *testing.T,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply monad right identity law
|
||||||
|
//
|
||||||
|
// M.chain(fa, M.of) <-> fa
|
||||||
|
func MonadAssertRightIdentity[HKTA, HKTAA, A any](t *testing.T,
|
||||||
|
eq E.Eq[HKTA],
|
||||||
|
|
||||||
|
ma monad.Monad[A, A, HKTA, HKTA, HKTAA],
|
||||||
|
|
||||||
|
) func(fa HKTA) bool {
|
||||||
|
return func(fa HKTA) bool {
|
||||||
|
|
||||||
|
left := ma.Chain(ma.Of)(fa)
|
||||||
|
right := fa
|
||||||
|
|
||||||
|
return assert.True(t, eq.Equals(left, right), "Monad right identity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange', `associativity`, `left identity`, `right identity`
|
// AssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange', `associativity`, `left identity`, `right identity`
|
||||||
|
//
|
||||||
|
// Deprecated: use [MonadAssertLaws] instead
|
||||||
func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
|
func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
|
||||||
eqa E.Eq[HKTA],
|
eqa E.Eq[HKTA],
|
||||||
eqb E.Eq[HKTB],
|
eqb E.Eq[HKTB],
|
||||||
@@ -120,3 +174,55 @@ func AssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A
|
|||||||
return applicative(a) && chain(fa) && leftIdentity(a) && rightIdentity(fa)
|
return applicative(a) && chain(fa) && leftIdentity(a) && rightIdentity(fa)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MonadAssertLaws asserts the apply laws `identity`, `composition`, `associative composition`, 'applicative identity', 'homomorphism', 'interchange', `associativity`, `left identity`, `right identity`
|
||||||
|
func MonadAssertLaws[HKTA, HKTB, HKTC, HKTAA, HKTAB, HKTBC, HKTAC, HKTABB, HKTABAC, A, B, C any](t *testing.T,
|
||||||
|
eqa E.Eq[HKTA],
|
||||||
|
eqb E.Eq[HKTB],
|
||||||
|
eqc E.Eq[HKTC],
|
||||||
|
|
||||||
|
fofc pointed.Pointed[C, HKTC],
|
||||||
|
fofaa pointed.Pointed[func(A) A, HKTAA],
|
||||||
|
fofbc pointed.Pointed[func(B) C, HKTBC],
|
||||||
|
fofabb pointed.Pointed[func(func(A) B) B, HKTABB],
|
||||||
|
|
||||||
|
fmap functor.Functor[func(B) C, func(func(A) B) func(A) C, HKTBC, HKTABAC],
|
||||||
|
|
||||||
|
fapabb applicative.Applicative[func(A) B, B, HKTAB, HKTB, HKTABB],
|
||||||
|
fapabac applicative.Applicative[func(A) B, func(A) C, HKTAB, HKTAC, HKTABAC],
|
||||||
|
|
||||||
|
maa monad.Monad[A, A, HKTA, HKTA, HKTAA],
|
||||||
|
mab monad.Monad[A, B, HKTA, HKTB, HKTAB],
|
||||||
|
mac monad.Monad[A, C, HKTA, HKTC, HKTAC],
|
||||||
|
mbc monad.Monad[B, C, HKTB, HKTC, HKTBC],
|
||||||
|
|
||||||
|
ab func(A) B,
|
||||||
|
bc func(B) C,
|
||||||
|
) func(a A) bool {
|
||||||
|
// derivations
|
||||||
|
fofa := monad.ToPointed(maa)
|
||||||
|
fofb := monad.ToPointed(mbc)
|
||||||
|
fofab := applicative.ToPointed(fapabb)
|
||||||
|
fapaa := monad.ToApplicative(maa)
|
||||||
|
fapab := monad.ToApplicative(mab)
|
||||||
|
chainab := monad.ToChainable(mab)
|
||||||
|
chainac := monad.ToChainable(mac)
|
||||||
|
chainbc := monad.ToChainable(mbc)
|
||||||
|
fapbc := chain.ToApply(chainbc)
|
||||||
|
fapac := chain.ToApply(chainac)
|
||||||
|
|
||||||
|
faa := monad.ToFunctor(maa)
|
||||||
|
|
||||||
|
// applicative laws
|
||||||
|
apLaw := LA.ApplicativeAssertLaws(t, eqa, eqb, eqc, fofb, fofaa, fofbc, fofabb, faa, fmap, fapaa, fapab, fapbc, fapac, fapabb, fapabac, ab, bc)
|
||||||
|
// chain laws
|
||||||
|
chainLaw := LC.ChainAssertLaws(t, eqa, eqc, fofb, fofc, fofab, fofbc, faa, fmap, chainab, chainac, chainbc, applicative.ToApply(fapabac), ab, bc)
|
||||||
|
// monad laws
|
||||||
|
leftIdentity := MonadAssertLeftIdentity(t, eqb, fofb, mab, ab)
|
||||||
|
rightIdentity := MonadAssertRightIdentity(t, eqa, maa)
|
||||||
|
|
||||||
|
return func(a A) bool {
|
||||||
|
fa := fofa.Of(a)
|
||||||
|
return apLaw(a) && chainLaw(fa) && leftIdentity(a) && rightIdentity(fa)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -124,17 +124,22 @@ func ReadJson[A any](client Client) func(Requester) IOE.IOEither[error, A] {
|
|||||||
return ReadJSON[A](client)
|
return ReadJSON[A](client)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadJSON sends a request, reads the response and parses the response as JSON
|
// readJSON sends a request, reads the response and parses the response as a []byte
|
||||||
func ReadJSON[A any](client Client) func(Requester) IOE.IOEither[error, A] {
|
func readJSON(client Client) func(Requester) IOE.IOEither[error, []byte] {
|
||||||
return F.Flow3(
|
return F.Flow3(
|
||||||
ReadFullResponse(client),
|
ReadFullResponse(client),
|
||||||
IOE.ChainFirstEitherK(F.Flow2(
|
IOE.ChainFirstEitherK(F.Flow2(
|
||||||
H.Response,
|
H.Response,
|
||||||
H.ValidateJSONResponse,
|
H.ValidateJSONResponse,
|
||||||
)),
|
)),
|
||||||
IOE.ChainEitherK(F.Flow2(
|
IOE.Map[error](H.Body),
|
||||||
H.Body,
|
)
|
||||||
J.Unmarshal[A],
|
}
|
||||||
)),
|
|
||||||
|
// ReadJSON sends a request, reads the response and parses the response as JSON
|
||||||
|
func ReadJSON[A any](client Client) func(Requester) IOE.IOEither[error, A] {
|
||||||
|
return F.Flow2(
|
||||||
|
readJSON(client),
|
||||||
|
IOE.ChainEitherK[error](J.Unmarshal[A]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -113,7 +113,7 @@ func fromPredicate[S, A any](creator func(get func(S) O.Option[A], set func(S, A
|
|||||||
return func(get func(S) A, set func(S, A) S) Optional[S, A] {
|
return func(get func(S) A, set func(S, A) S) Optional[S, A] {
|
||||||
return creator(
|
return creator(
|
||||||
F.Flow2(get, fromPred),
|
F.Flow2(get, fromPred),
|
||||||
func(s S, a A) S {
|
func(s S, _ A) S {
|
||||||
return F.Pipe3(
|
return F.Pipe3(
|
||||||
s,
|
s,
|
||||||
get,
|
get,
|
||||||
|
33
pair/eq.go
Normal file
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