mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
initial checkin
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
14
either/apply.go
Normal file
14
either/apply.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
S "github.com/ibm/fp-go/semigroup"
|
||||
)
|
||||
|
||||
func ApplySemigroup[E, A any](s S.Semigroup[A]) S.Semigroup[Either[E, A]] {
|
||||
return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[E, A, A], s)
|
||||
}
|
||||
|
||||
func ApplicativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
||||
return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[E, A, A], m)
|
||||
}
|
48
either/apply_test.go
Normal file
48
either/apply_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
M "github.com/ibm/fp-go/monoid/testing"
|
||||
N "github.com/ibm/fp-go/number"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestApplySemigroup(t *testing.T) {
|
||||
|
||||
sg := ApplySemigroup[string](N.SemigroupSum[int]())
|
||||
|
||||
la := Left[string, int]("a")
|
||||
lb := Left[string, int]("b")
|
||||
r1 := Right[string](1)
|
||||
r2 := Right[string](2)
|
||||
r3 := Right[string](3)
|
||||
|
||||
assert.Equal(t, la, sg.Concat(la, lb))
|
||||
assert.Equal(t, lb, sg.Concat(r1, lb))
|
||||
assert.Equal(t, la, sg.Concat(la, r2))
|
||||
assert.Equal(t, lb, sg.Concat(r1, lb))
|
||||
assert.Equal(t, r3, sg.Concat(r1, r2))
|
||||
}
|
||||
|
||||
func TestApplicativeMonoid(t *testing.T) {
|
||||
|
||||
m := ApplicativeMonoid[string](N.MonoidSum[int]())
|
||||
|
||||
la := Left[string, int]("a")
|
||||
lb := Left[string, int]("b")
|
||||
r1 := Right[string](1)
|
||||
r2 := Right[string](2)
|
||||
r3 := Right[string](3)
|
||||
|
||||
assert.Equal(t, la, m.Concat(la, m.Empty()))
|
||||
assert.Equal(t, lb, m.Concat(m.Empty(), lb))
|
||||
assert.Equal(t, r1, m.Concat(r1, m.Empty()))
|
||||
assert.Equal(t, r2, m.Concat(m.Empty(), r2))
|
||||
assert.Equal(t, r3, m.Concat(r1, r2))
|
||||
}
|
||||
|
||||
func TestApplicativeMonoidLaws(t *testing.T) {
|
||||
m := ApplicativeMonoid[string](N.MonoidSum[int]())
|
||||
M.AssertLaws(t, m)([]Either[string, int]{Left[string, int]("a"), Right[string](1)})
|
||||
}
|
61
either/curry.go
Normal file
61
either/curry.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// these function curry a golang function that returns an error into its curried version that returns an either
|
||||
|
||||
func Curry0[R any](f func() (R, error)) func() Either[error, R] {
|
||||
return Eitherize0(f)
|
||||
}
|
||||
|
||||
func Curry1[T1, R any](f func(T1) (R, error)) func(T1) Either[error, R] {
|
||||
return Eitherize1(f)
|
||||
}
|
||||
|
||||
func Curry2[T1, T2, R any](f func(T1, T2) (R, error)) func(T1) func(T2) Either[error, R] {
|
||||
return F.Curry2(Eitherize2(f))
|
||||
}
|
||||
|
||||
func Curry3[T1, T2, T3, R any](f func(T1, T2, T3) (R, error)) func(T1) func(T2) func(T3) Either[error, R] {
|
||||
return F.Curry3(Eitherize3(f))
|
||||
}
|
||||
|
||||
func Curry4[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) (R, error)) func(T1) func(T2) func(T3) func(T4) Either[error, R] {
|
||||
return F.Curry4(Eitherize4(f))
|
||||
}
|
||||
|
||||
func Uncurry0[R any](f func() Either[error, R]) func() (R, error) {
|
||||
return func() (R, error) {
|
||||
return UnwrapError(f())
|
||||
}
|
||||
}
|
||||
|
||||
func Uncurry1[T1, R any](f func(T1) Either[error, R]) func(T1) (R, error) {
|
||||
uc := F.Uncurry1(f)
|
||||
return func(t1 T1) (R, error) {
|
||||
return UnwrapError(uc(t1))
|
||||
}
|
||||
}
|
||||
|
||||
func Uncurry2[T1, T2, R any](f func(T1) func(T2) Either[error, R]) func(T1, T2) (R, error) {
|
||||
uc := F.Uncurry2(f)
|
||||
return func(t1 T1, t2 T2) (R, error) {
|
||||
return UnwrapError(uc(t1, t2))
|
||||
}
|
||||
}
|
||||
|
||||
func Uncurry3[T1, T2, T3, R any](f func(T1) func(T2) func(T3) Either[error, R]) func(T1, T2, T3) (R, error) {
|
||||
uc := F.Uncurry3(f)
|
||||
return func(t1 T1, t2 T2, t3 T3) (R, error) {
|
||||
return UnwrapError(uc(t1, t2, t3))
|
||||
}
|
||||
}
|
||||
|
||||
func Uncurry4[T1, T2, T3, T4, R any](f func(T1) func(T2) func(T3) func(T4) Either[error, R]) func(T1, T2, T3, T4) (R, error) {
|
||||
uc := F.Uncurry4(f)
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error) {
|
||||
return UnwrapError(uc(t1, t2, t3, t4))
|
||||
}
|
||||
}
|
344
either/either.go
Normal file
344
either/either.go
Normal file
@@ -0,0 +1,344 @@
|
||||
// package either implements the Either monad
|
||||
//
|
||||
// A data type that can be of either of two types but not both. This is
|
||||
// typically used to carry an error or a return value
|
||||
package either
|
||||
|
||||
import (
|
||||
E "github.com/ibm/fp-go/errors"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
func Of[E, A any](value A) Either[E, A] {
|
||||
return F.Pipe1(value, Right[E, A])
|
||||
}
|
||||
|
||||
func FromIO[E, A any](f func() A) Either[E, A] {
|
||||
return F.Pipe1(f(), Right[E, A])
|
||||
}
|
||||
|
||||
func MonadAp[E, A, B any](fab Either[E, func(a A) B], fa Either[E, A]) Either[E, B] {
|
||||
return fold(fab, Left[E, B], func(ab func(A) B) Either[E, B] {
|
||||
return fold(fa, Left[E, B], F.Flow2(ab, Right[E, B]))
|
||||
})
|
||||
}
|
||||
|
||||
func Ap[E, A, B any](fa Either[E, A]) func(fab Either[E, func(a A) B]) Either[E, B] {
|
||||
return F.Bind2nd(MonadAp[E, A, B], fa)
|
||||
}
|
||||
|
||||
func MonadMap[E, A, B any](fa Either[E, A], f func(a A) B) Either[E, B] {
|
||||
return MonadChain(fa, F.Flow2(f, Right[E, B]))
|
||||
}
|
||||
|
||||
func MonadBiMap[E1, E2, A, B any](fa Either[E1, A], f func(E1) E2, g func(a A) B) Either[E2, B] {
|
||||
return fold(fa, F.Flow2(f, Left[E2, B]), F.Flow2(g, Right[E2, B]))
|
||||
}
|
||||
|
||||
// BiMap maps a pair of functions over the two type arguments of the bifunctor.
|
||||
func BiMap[E1, E2, A, B any](f func(E1) E2, g func(a A) B) func(Either[E1, A]) Either[E2, B] {
|
||||
return Fold(F.Flow2(f, Left[E2, B]), F.Flow2(g, Right[E2, B]))
|
||||
}
|
||||
|
||||
func MonadMapTo[E, A, B any](fa Either[E, A], b B) Either[E, B] {
|
||||
return MonadMap(fa, F.Constant1[A](b))
|
||||
}
|
||||
|
||||
func MapTo[E, A, B any](b B) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadMapTo[E, A, B], b)
|
||||
}
|
||||
|
||||
func MonadMapLeft[E, A, B any](fa Either[E, A], f func(E) B) Either[B, A] {
|
||||
return fold(fa, F.Flow2(f, Left[B, A]), Right[B, A])
|
||||
}
|
||||
|
||||
func Map[E, A, B any](f func(a A) B) func(fa Either[E, A]) Either[E, B] {
|
||||
return Chain(F.Flow2(f, Right[E, B]))
|
||||
}
|
||||
|
||||
func MapLeft[E, A, B any](f func(E) B) func(fa Either[E, A]) Either[B, A] {
|
||||
return F.Bind2nd(MonadMapLeft[E, A, B], f)
|
||||
}
|
||||
|
||||
func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] {
|
||||
return fold(fa, Left[E, B], f)
|
||||
}
|
||||
|
||||
func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] {
|
||||
return MonadChain(ma, func(a A) Either[E, A] {
|
||||
return MonadMap(f(a), F.Constant1[B](a))
|
||||
})
|
||||
}
|
||||
|
||||
func MonadChainTo[E, A, B any](ma Either[E, A], mb Either[E, B]) Either[E, B] {
|
||||
return mb
|
||||
}
|
||||
|
||||
func MonadChainOptionK[E, A, B any](onNone func() E, ma Either[E, A], f func(A) O.Option[B]) Either[E, B] {
|
||||
return MonadChain(ma, F.Flow2(f, FromOption[E, B](onNone)))
|
||||
}
|
||||
|
||||
func ChainOptionK[E, A, B any](onNone func() E) func(func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
|
||||
from := FromOption[E, B](onNone)
|
||||
return func(f func(A) O.Option[B]) func(Either[E, A]) Either[E, B] {
|
||||
return Chain(F.Flow2(f, from))
|
||||
}
|
||||
}
|
||||
|
||||
func ChainTo[E, A, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadChainTo[E, A, B], mb)
|
||||
}
|
||||
|
||||
func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] {
|
||||
return F.Bind2nd(MonadChain[E, A, B], f)
|
||||
}
|
||||
|
||||
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
|
||||
return F.Bind2nd(MonadChainFirst[E, A, B], f)
|
||||
}
|
||||
|
||||
func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] {
|
||||
return MonadChain(mma, F.Identity[Either[E, A]])
|
||||
}
|
||||
|
||||
func TryCatch[E, A any](f func() (A, error), onThrow func(error) E) Either[E, A] {
|
||||
val, err := f()
|
||||
if err != nil {
|
||||
return F.Pipe2(err, onThrow, Left[E, A])
|
||||
}
|
||||
return F.Pipe1(val, Right[E, A])
|
||||
}
|
||||
|
||||
func TryCatchErrorG[GA ~func() (A, error), A any](f GA) Either[error, A] {
|
||||
return TryCatch(f, E.IdentityError)
|
||||
}
|
||||
|
||||
func TryCatchError[A any](f func() (A, error)) Either[error, A] {
|
||||
return TryCatchErrorG(f)
|
||||
}
|
||||
|
||||
func Sequence2[E, T1, T2, R any](f func(T1, T2) Either[E, R]) func(Either[E, T1], Either[E, T2]) Either[E, R] {
|
||||
return func(e1 Either[E, T1], e2 Either[E, T2]) Either[E, R] {
|
||||
return MonadSequence2(e1, e2, f)
|
||||
}
|
||||
}
|
||||
|
||||
func Sequence3[E, T1, T2, T3, R any](f func(T1, T2, T3) Either[E, R]) func(Either[E, T1], Either[E, T2], Either[E, T3]) Either[E, R] {
|
||||
return func(e1 Either[E, T1], e2 Either[E, T2], e3 Either[E, T3]) Either[E, R] {
|
||||
return MonadSequence3(e1, e2, e3, f)
|
||||
}
|
||||
}
|
||||
|
||||
func FromOption[E, A any](onNone func() E) func(O.Option[A]) Either[E, A] {
|
||||
return O.Fold(F.Nullary2(onNone, Left[E, A]), Right[E, A])
|
||||
}
|
||||
|
||||
func ToOption[E, A any]() func(Either[E, A]) O.Option[A] {
|
||||
return Fold(F.Ignore1[E](O.None[A]), O.Some[A])
|
||||
}
|
||||
|
||||
func FromError[A any](f func(a A) error) func(A) Either[error, A] {
|
||||
return func(a A) Either[error, A] {
|
||||
return TryCatchError(func() (A, error) {
|
||||
return a, f(a)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ToError[A any](e Either[error, A]) error {
|
||||
return fold(e, E.IdentityError, F.Constant1[A, error](nil))
|
||||
}
|
||||
|
||||
func Eitherize0G[GA ~func() (R, error), GB ~func() Either[error, R], R any](f GA) GB {
|
||||
return F.Bind1(TryCatchErrorG[GA, R], f)
|
||||
}
|
||||
|
||||
func Eitherize0[R any](f func() (R, error)) func() Either[error, R] {
|
||||
return Eitherize0G[func() (R, error), func() Either[error, R]](f)
|
||||
}
|
||||
|
||||
func Uneitherize0G[GA ~func() Either[error, R], GB ~func() (R, error), R any](f GA) GB {
|
||||
return func() (R, error) {
|
||||
return UnwrapError(f())
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize0[R any](f func() Either[error, R]) func() (R, error) {
|
||||
return Uneitherize0G[func() Either[error, R], func() (R, error)](f)
|
||||
}
|
||||
|
||||
func Eitherize1G[GA ~func(T1) (R, error), GB ~func(T1) Either[error, R], T1, R any](f GA) GB {
|
||||
return func(t1 T1) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Eitherize1[T1, R any](f func(T1) (R, error)) func(T1) Either[error, R] {
|
||||
return Eitherize1G[func(T1) (R, error), func(T1) Either[error, R]](f)
|
||||
}
|
||||
|
||||
func Uneitherize1G[GA ~func(T1) Either[error, R], GB ~func(T1) (R, error), T1, R any](f GA) GB {
|
||||
return func(t1 T1) (R, error) {
|
||||
return UnwrapError(f(t1))
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize1[T1, R any](f func(T1) Either[error, R]) func(T1) (R, error) {
|
||||
return Uneitherize1G[func(T1) Either[error, R], func(T1) (R, error)](f)
|
||||
}
|
||||
|
||||
func Eitherize2G[GA ~func(t1 T1, t2 T2) (R, error), GB ~func(t1 T1, t2 T2) Either[error, R], T1, T2, R any](f GA) GB {
|
||||
return func(t1 T1, t2 T2) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Eitherize2[T1, T2, R any](f func(t1 T1, t2 T2) (R, error)) func(t1 T1, t2 T2) Either[error, R] {
|
||||
return Eitherize2G[func(t1 T1, t2 T2) (R, error), func(t1 T1, t2 T2) Either[error, R]](f)
|
||||
}
|
||||
|
||||
func Uneitherize2G[GA ~func(T1, T2) Either[error, R], GB ~func(T1, T2) (R, error), T1, T2, R any](f GA) GB {
|
||||
return func(t1 T1, t2 T2) (R, error) {
|
||||
return UnwrapError(f(t1, t2))
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize2[T1, T2, R any](f func(T1, T2) Either[error, R]) func(T1, T2) (R, error) {
|
||||
return Uneitherize2G[func(T1, T2) Either[error, R], func(T1, T2) (R, error)](f)
|
||||
}
|
||||
|
||||
func Eitherize3G[GA ~func(t1 T1, t2 T2, t3 T3) (R, error), GB ~func(t1 T1, t2 T2, t3 T3) Either[error, R], T1, T2, T3, R any](f GA) GB {
|
||||
return func(t1 T1, t2 T2, t3 T3) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Eitherize3[T1, T2, T3, R any](f func(t1 T1, t2 T2, t3 T3) (R, error)) func(t1 T1, t2 T2, t3 T3) Either[error, R] {
|
||||
return Eitherize3G[func(t1 T1, t2 T2, t3 T3) (R, error), func(t1 T1, t2 T2, t3 T3) Either[error, R]](f)
|
||||
}
|
||||
|
||||
func Uneitherize3G[GA ~func(T1, T2, T3) Either[error, R], GB ~func(T1, T2, T3) (R, error), T1, T2, T3, R any](f GA) GB {
|
||||
return func(t1 T1, t2 T2, t3 T3) (R, error) {
|
||||
return UnwrapError(f(t1, t2, t3))
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize3[T1, T2, T3, R any](f func(T1, T2, T3) Either[error, R]) func(T1, T2, T3) (R, error) {
|
||||
return Uneitherize3G[func(T1, T2, T3) Either[error, R], func(T1, T2, T3) (R, error)](f)
|
||||
}
|
||||
|
||||
func Eitherize4G[GA ~func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error), GB ~func(t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R], T1, T2, T3, T4, R any](f GA) GB {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, t4)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Eitherize4[T1, T2, T3, T4, R any](f func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error)) func(t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R] {
|
||||
return Eitherize4G[func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error), func(t1 T1, t2 T2, t3 T3, t4 T4) Either[error, R]](f)
|
||||
}
|
||||
|
||||
func Uneitherize4G[GA ~func(T1, T2, T3, T4) Either[error, R], GB ~func(T1, T2, T3, T4) (R, error), T1, T2, T3, T4, R any](f GA) GB {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4) (R, error) {
|
||||
return UnwrapError(f(t1, t2, t3, t4))
|
||||
}
|
||||
}
|
||||
|
||||
func Uneitherize4[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) Either[error, R]) func(T1, T2, T3, T4) (R, error) {
|
||||
return Uneitherize4G[func(T1, T2, T3, T4) Either[error, R], func(T1, T2, T3, T4) (R, error)](f)
|
||||
}
|
||||
|
||||
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
|
||||
return fold(ma, onLeft, onRight)
|
||||
}
|
||||
|
||||
func Fold[E, A, B any](onLeft func(E) B, onRight func(A) B) func(Either[E, A]) B {
|
||||
return func(ma Either[E, A]) B {
|
||||
return fold(ma, onLeft, onRight)
|
||||
}
|
||||
}
|
||||
|
||||
func UnwrapError[A any](ma Either[error, A]) (A, error) {
|
||||
return Unwrap[error](ma)
|
||||
}
|
||||
|
||||
func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Either[E, A] {
|
||||
return func(a A) Either[E, A] {
|
||||
if pred(a) {
|
||||
return Right[E](a)
|
||||
}
|
||||
return Left[E, A](onFalse(a))
|
||||
}
|
||||
}
|
||||
|
||||
func FromNillable[E, A any](e E) func(*A) Either[E, *A] {
|
||||
return FromPredicate(F.IsNonNil[A], F.Constant1[*A](e))
|
||||
}
|
||||
|
||||
func GetOrElse[E, A any](onLeft func(E) A) func(Either[E, A]) A {
|
||||
return Fold(onLeft, F.Identity[A])
|
||||
}
|
||||
|
||||
func Reduce[E, A, B any](f func(B, A) B, initial B) func(Either[E, A]) B {
|
||||
return Fold(
|
||||
F.Constant1[E](initial),
|
||||
F.Bind1st(f, initial),
|
||||
)
|
||||
}
|
||||
|
||||
func AltW[E, E1, A any](that func() Either[E1, A]) func(Either[E, A]) Either[E1, A] {
|
||||
return Fold(F.Ignore1[E](that), Right[E1, A])
|
||||
}
|
||||
|
||||
func Alt[E, A any](that func() Either[E, A]) func(Either[E, A]) Either[E, A] {
|
||||
return AltW[E](that)
|
||||
}
|
||||
|
||||
func OrElse[E, A any](onLeft func(e E) Either[E, A]) func(Either[E, A]) Either[E, A] {
|
||||
return Fold(onLeft, Of[E, A])
|
||||
}
|
||||
|
||||
func ToType[E, A any](onError func(any) E) func(any) Either[E, A] {
|
||||
return func(value any) Either[E, A] {
|
||||
return F.Pipe2(
|
||||
value,
|
||||
O.ToType[A],
|
||||
O.Fold(F.Nullary3(F.Constant(value), onError, Left[E, A]), Right[E, A]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func Memoize[E, A any](val Either[E, A]) Either[E, A] {
|
||||
return val
|
||||
}
|
||||
|
||||
func MonadSequence2[E, T1, T2, R any](e1 Either[E, T1], e2 Either[E, T2], f func(T1, T2) Either[E, R]) Either[E, R] {
|
||||
return fold(e1, Left[E, R], func(t1 T1) Either[E, R] {
|
||||
return fold(e2, Left[E, R], func(t2 T2) Either[E, R] {
|
||||
return f(t1, t2)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func MonadSequence3[E, T1, T2, T3, R any](e1 Either[E, T1], e2 Either[E, T2], e3 Either[E, T3], f func(T1, T2, T3) Either[E, R]) Either[E, R] {
|
||||
return fold(e1, Left[E, R], func(t1 T1) Either[E, R] {
|
||||
return fold(e2, Left[E, R], func(t2 T2) Either[E, R] {
|
||||
return fold(e3, Left[E, R], func(t3 T3) Either[E, R] {
|
||||
return f(t1, t2, t3)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Swap changes the order of type parameters
|
||||
func Swap[E, A any](val Either[E, A]) Either[A, E] {
|
||||
return fold(val, Right[A, E], Left[A, E])
|
||||
}
|
96
either/either_test.go
Normal file
96
either/either_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/utils"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
S "github.com/ibm/fp-go/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsLeft(t *testing.T) {
|
||||
err := errors.New("Some error")
|
||||
withError := Left[error, string](err)
|
||||
|
||||
assert.True(t, IsLeft(withError))
|
||||
assert.False(t, IsRight(withError))
|
||||
}
|
||||
|
||||
func TestIsRight(t *testing.T) {
|
||||
noError := Right[error]("Carsten")
|
||||
|
||||
assert.True(t, IsRight(noError))
|
||||
assert.False(t, IsLeft(noError))
|
||||
}
|
||||
|
||||
func TestMapEither(t *testing.T) {
|
||||
|
||||
assert.Equal(t, F.Pipe1(Right[error]("abc"), Map[error](utils.StringLen)), Right[error](3))
|
||||
|
||||
val2 := F.Pipe1(Left[string, string]("s"), Map[string](utils.StringLen))
|
||||
exp2 := Left[string, int]("s")
|
||||
|
||||
assert.Equal(t, val2, exp2)
|
||||
}
|
||||
|
||||
func TestUnwrapError(t *testing.T) {
|
||||
a := ""
|
||||
err := errors.New("Some error")
|
||||
withError := Left[error, string](err)
|
||||
|
||||
res, extracted := UnwrapError(withError)
|
||||
assert.Equal(t, a, res)
|
||||
assert.Equal(t, extracted, err)
|
||||
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
|
||||
s := S.Semigroup()
|
||||
|
||||
assert.Equal(t, "foobar", F.Pipe1(Right[string]("bar"), Reduce[string](s.Concat, "foo")))
|
||||
assert.Equal(t, "foo", F.Pipe1(Left[string, string]("bar"), Reduce[string](s.Concat, "foo")))
|
||||
|
||||
}
|
||||
|
||||
func TestAp(t *testing.T) {
|
||||
f := S.Size
|
||||
|
||||
assert.Equal(t, Right[string](3), F.Pipe1(Right[string](f), Ap[string, string, int](Right[string]("abc"))))
|
||||
assert.Equal(t, Left[string, int]("maError"), F.Pipe1(Right[string](f), Ap[string, string, int](Left[string, string]("maError"))))
|
||||
assert.Equal(t, Left[string, int]("mabError"), F.Pipe1(Left[string, func(string) int]("mabError"), Ap[string, string, int](Left[string, string]("maError"))))
|
||||
}
|
||||
|
||||
func TestAlt(t *testing.T) {
|
||||
assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Right[string](2)))))
|
||||
assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Left[string, int]("a")))))
|
||||
assert.Equal(t, Right[string](2), F.Pipe1(Left[string, int]("b"), Alt(F.Constant(Right[string](2)))))
|
||||
assert.Equal(t, Left[string, int]("b"), F.Pipe1(Left[string, int]("a"), Alt(F.Constant(Left[string, int]("b")))))
|
||||
}
|
||||
|
||||
func TestChainFirst(t *testing.T) {
|
||||
f := F.Flow2(S.Size, Right[string, int])
|
||||
|
||||
assert.Equal(t, Right[string]("abc"), F.Pipe1(Right[string]("abc"), ChainFirst(f)))
|
||||
assert.Equal(t, Left[string, string]("maError"), F.Pipe1(Left[string, string]("maError"), ChainFirst(f)))
|
||||
}
|
||||
|
||||
func TestChainOptionK(t *testing.T) {
|
||||
f := ChainOptionK[string, int, int](F.Constant("a"))(func(n int) O.Option[int] {
|
||||
if n > 0 {
|
||||
return O.Some(n)
|
||||
}
|
||||
return O.None[int]()
|
||||
})
|
||||
assert.Equal(t, Right[string](1), f(Right[string](1)))
|
||||
assert.Equal(t, Left[string, int]("a"), f(Right[string](-1)))
|
||||
assert.Equal(t, Left[string, int]("b"), f(Left[string, int]("b")))
|
||||
}
|
||||
|
||||
func TestFromOption(t *testing.T) {
|
||||
assert.Equal(t, Left[string, int]("none"), FromOption[string, int](F.Constant("none"))(O.None[int]()))
|
||||
assert.Equal(t, Right[string](1), FromOption[string, int](F.Constant("none"))(O.Some(1)))
|
||||
}
|
25
either/eq.go
Normal file
25
either/eq.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// Constructs an equal predicate for an `Either`
|
||||
func Eq[E, A any](e EQ.Eq[E], a EQ.Eq[A]) EQ.Eq[Either[E, A]] {
|
||||
// some convenient shortcuts
|
||||
eqa := F.Curry2(a.Equals)
|
||||
eqe := F.Curry2(e.Equals)
|
||||
|
||||
fca := F.Bind2nd(Fold[E, A, bool], F.Constant1[A](false))
|
||||
fce := F.Bind1st(Fold[E, A, bool], F.Constant1[E](false))
|
||||
|
||||
fld := Fold(F.Flow2(eqe, fca), F.Flow2(eqa, fce))
|
||||
|
||||
return EQ.FromEquals(F.Uncurry2(fld))
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an `Eq` from the canonical comparison function
|
||||
func FromStrictEquals[E, A comparable]() EQ.Eq[Either[E, A]] {
|
||||
return Eq(EQ.FromStrictEquals[E](), EQ.FromStrictEquals[A]())
|
||||
}
|
30
either/eq_test.go
Normal file
30
either/eq_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEq(t *testing.T) {
|
||||
|
||||
r1 := Of[string](1)
|
||||
r2 := Of[string](1)
|
||||
r3 := Of[string](2)
|
||||
|
||||
e1 := Left[string, int]("a")
|
||||
e2 := Left[string, int]("a")
|
||||
e3 := Left[string, int]("b")
|
||||
|
||||
eq := FromStrictEquals[string, int]()
|
||||
|
||||
assert.True(t, eq.Equals(r1, r1))
|
||||
assert.True(t, eq.Equals(r1, r2))
|
||||
assert.False(t, eq.Equals(r1, r3))
|
||||
assert.False(t, eq.Equals(r1, e1))
|
||||
|
||||
assert.True(t, eq.Equals(e1, e1))
|
||||
assert.True(t, eq.Equals(e1, e2))
|
||||
assert.False(t, eq.Equals(e1, e3))
|
||||
assert.False(t, eq.Equals(e2, r2))
|
||||
}
|
23
either/exec/exec.go
Normal file
23
either/exec/exec.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package Exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
"github.com/ibm/fp-go/exec"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
GE "github.com/ibm/fp-go/internal/exec"
|
||||
)
|
||||
|
||||
var (
|
||||
// Command executes a command
|
||||
// use this version if the command does not produce any side effect, i.e. if the output is uniquely determined by by the input
|
||||
// typically you'd rather use the IOEither version of the command
|
||||
Command = F.Curry3(command)
|
||||
)
|
||||
|
||||
func command(name string, args []string, in []byte) E.Either[error, exec.CommandOutput] {
|
||||
return E.TryCatchError(func() (exec.CommandOutput, error) {
|
||||
return GE.Exec(context.Background(), name, args, in)
|
||||
})
|
||||
}
|
36
either/http/request.go
Normal file
36
either/http/request.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package Http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
|
||||
E "github.com/ibm/fp-go/either"
|
||||
)
|
||||
|
||||
var (
|
||||
PostRequest = bodyRequest("POST")
|
||||
PutRequest = bodyRequest("PUT")
|
||||
|
||||
GetRequest = noBodyRequest("GET")
|
||||
DeleteRequest = noBodyRequest("DELETE")
|
||||
OptionsRequest = noBodyRequest("OPTIONS")
|
||||
HeadRequest = noBodyRequest("HEAD")
|
||||
)
|
||||
|
||||
func bodyRequest(method string) func(string) func([]byte) E.Either[error, *http.Request] {
|
||||
return func(url string) func([]byte) E.Either[error, *http.Request] {
|
||||
return func(body []byte) E.Either[error, *http.Request] {
|
||||
return E.TryCatchError(func() (*http.Request, error) {
|
||||
return http.NewRequest(method, url, bytes.NewReader(body))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func noBodyRequest(method string) func(string) E.Either[error, *http.Request] {
|
||||
return func(url string) E.Either[error, *http.Request] {
|
||||
return E.TryCatchError(func() (*http.Request, error) {
|
||||
return http.NewRequest(method, url, nil)
|
||||
})
|
||||
}
|
||||
}
|
55
either/legacy.go
Normal file
55
either/legacy.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package either
|
||||
|
||||
import "fmt"
|
||||
|
||||
type EitherTag int
|
||||
|
||||
const (
|
||||
LeftTag EitherTag = iota
|
||||
RightTag
|
||||
)
|
||||
|
||||
// Either defines a data structure that logically holds either an E or an A. The tag discriminates the cases
|
||||
type Either[E, A any] struct {
|
||||
Tag EitherTag
|
||||
Left E
|
||||
Right A
|
||||
}
|
||||
|
||||
// String prints some debug info for the object
|
||||
func (s Either[E, A]) String() string {
|
||||
switch s.Tag {
|
||||
case LeftTag:
|
||||
return fmt.Sprintf("Left[%T, %T](%v)", s.Left, s.Right, s.Left)
|
||||
case RightTag:
|
||||
return fmt.Sprintf("Right[%T, %T](%v)", s.Left, s.Right, s.Right)
|
||||
}
|
||||
return "Invalid"
|
||||
}
|
||||
|
||||
func IsLeft[E, A any](val Either[E, A]) bool {
|
||||
return val.Tag == LeftTag
|
||||
}
|
||||
|
||||
func IsRight[E, A any](val Either[E, A]) bool {
|
||||
return val.Tag == RightTag
|
||||
}
|
||||
|
||||
func Left[E, A any](value E) Either[E, A] {
|
||||
return Either[E, A]{Tag: LeftTag, Left: value}
|
||||
}
|
||||
|
||||
func Right[E, A any](value A) Either[E, A] {
|
||||
return Either[E, A]{Tag: RightTag, Right: value}
|
||||
}
|
||||
|
||||
func fold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
|
||||
if IsLeft(ma) {
|
||||
return onLeft(ma.Left)
|
||||
}
|
||||
return onRight(ma.Right)
|
||||
}
|
||||
|
||||
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
|
||||
return ma.Right, ma.Left
|
||||
}
|
33
either/logger.go
Normal file
33
either/logger.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
L "github.com/ibm/fp-go/logging"
|
||||
)
|
||||
|
||||
func _log[E, A any](left func(string, ...any), right func(string, ...any), prefix string) func(Either[E, A]) Either[E, A] {
|
||||
return Fold(
|
||||
func(e E) Either[E, A] {
|
||||
left("%s: %v", prefix, e)
|
||||
return Left[E, A](e)
|
||||
},
|
||||
func(a A) Either[E, A] {
|
||||
right("%s: %v", prefix, a)
|
||||
return Right[E](a)
|
||||
})
|
||||
}
|
||||
|
||||
func Logger[E, A any](loggers ...*log.Logger) func(string) func(Either[E, A]) Either[E, A] {
|
||||
left, right := L.LoggingCallbacks(loggers...)
|
||||
return func(prefix string) func(Either[E, A]) Either[E, A] {
|
||||
delegate := _log[E, A](left, right, prefix)
|
||||
return func(ma Either[E, A]) Either[E, A] {
|
||||
return F.Pipe1(
|
||||
delegate(ma),
|
||||
ChainTo[E, A](ma),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
22
either/logger_test.go
Normal file
22
either/logger_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
|
||||
l := Logger[error, string]()
|
||||
|
||||
r := Right[error]("test")
|
||||
|
||||
res := F.Pipe1(
|
||||
r,
|
||||
l("out"),
|
||||
)
|
||||
|
||||
assert.Equal(t, r, res)
|
||||
}
|
69
either/modern._go
Normal file
69
either/modern._go
Normal file
@@ -0,0 +1,69 @@
|
||||
//go:build disabled
|
||||
|
||||
package either
|
||||
|
||||
// Either will either be E or A
|
||||
type Either[E, A any] interface {
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
type left[E any] struct {
|
||||
e E
|
||||
}
|
||||
|
||||
func (left[E]) IsLeft() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type right[A any] struct {
|
||||
a A
|
||||
}
|
||||
|
||||
func (right[A]) IsLeft() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l left[E]) String() string {
|
||||
return fmt.Sprintf("Left[%v]", l.e)
|
||||
}
|
||||
|
||||
func (r right[A]) String() string {
|
||||
return fmt.Sprintf("Right[%v]", r.a)
|
||||
}
|
||||
|
||||
func IsLeft[E, A any](val Either[E, A]) bool {
|
||||
switch any(val).(type) {
|
||||
case left[E]:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func IsRight[E, A any](val Either[E, A]) bool {
|
||||
return !IsLeft(val)
|
||||
}
|
||||
|
||||
func Left[E, A any](value E) Either[E, A] {
|
||||
return left[E]{e: value}
|
||||
}
|
||||
|
||||
func Right[E, A any](value A) Either[E, A] {
|
||||
return right[A]{a: value}
|
||||
}
|
||||
|
||||
func fold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
|
||||
if IsLeft(ma) {
|
||||
return onLeft(ma.(left[E]).e)
|
||||
}
|
||||
return onRight(ma.(right[A]).a)
|
||||
}
|
||||
|
||||
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
|
||||
if IsLeft(ma) {
|
||||
var a A
|
||||
return a, ma.(left[E]).e
|
||||
}
|
||||
var E e
|
||||
return ma.(right[A]).a, e
|
||||
}
|
30
either/record.go
Normal file
30
either/record.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
RR "github.com/ibm/fp-go/internal/record"
|
||||
)
|
||||
|
||||
// TraverseRecord transforms a record of options into an option of a record
|
||||
func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
||||
return RR.Traverse[GA](
|
||||
Of[E, GB],
|
||||
MonadMap[E, GB, func(B) GB],
|
||||
MonadAp[E, B, GB],
|
||||
f,
|
||||
)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms a record of eithers into an either of a record
|
||||
func TraverseRecord[K comparable, E, A, B any](f func(A) Either[E, B]) func(map[K]A) Either[E, map[K]B] {
|
||||
return TraverseRecordG[map[K]A, map[K]B](f)
|
||||
}
|
||||
|
||||
func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A any](ma GOA) Either[E, GA] {
|
||||
return TraverseRecordG[GOA, GA](F.Identity[Either[E, A]])(ma)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||
func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] {
|
||||
return SequenceRecordG[map[K]A](ma)
|
||||
}
|
29
either/resource.go
Normal file
29
either/resource.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
// constructs a function that creates a resource, then operates on it and then releases the resource
|
||||
func WithResource[E, R, A any](onCreate func() Either[E, R], onRelease func(R) Either[E, any]) func(func(R) Either[E, A]) Either[E, A] {
|
||||
|
||||
return func(f func(R) Either[E, A]) Either[E, A] {
|
||||
return MonadChain(
|
||||
onCreate(), func(r R) Either[E, A] {
|
||||
// run the code and make sure to release as quickly as possible
|
||||
res := f(r)
|
||||
released := onRelease(r)
|
||||
// handle the errors
|
||||
return fold(
|
||||
res,
|
||||
Left[E, A],
|
||||
func(a A) Either[E, A] {
|
||||
return F.Pipe1(
|
||||
released,
|
||||
MapTo[E, any](a),
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
39
either/resource_test.go
Normal file
39
either/resource_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWithResource(t *testing.T) {
|
||||
onCreate := func() Either[error, *os.File] {
|
||||
return TryCatchError(func() (*os.File, error) {
|
||||
return os.CreateTemp("", "*")
|
||||
})
|
||||
}
|
||||
onDelete := F.Flow2(
|
||||
func(f *os.File) Either[error, string] {
|
||||
return TryCatchError(func() (string, error) {
|
||||
return f.Name(), f.Close()
|
||||
})
|
||||
},
|
||||
Chain(func(name string) Either[error, any] {
|
||||
return TryCatchError(func() (any, error) {
|
||||
return name, os.Remove(name)
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
onHandler := func(f *os.File) Either[error, string] {
|
||||
return Of[error](f.Name())
|
||||
}
|
||||
|
||||
tempFile := WithResource[error, *os.File, string](onCreate, onDelete)
|
||||
|
||||
resE := tempFile(onHandler)
|
||||
|
||||
assert.True(t, IsRight(resE))
|
||||
}
|
45
either/sequence.go
Normal file
45
either/sequence.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
Apply "github.com/ibm/fp-go/apply"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
|
||||
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
||||
|
||||
func SequenceT1[E, A any](a Either[E, A]) Either[E, T.Tuple1[A]] {
|
||||
return Apply.SequenceT1(
|
||||
MonadMap[E, A, T.Tuple1[A]],
|
||||
a,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT2[E, A, B any](a Either[E, A], b Either[E, B]) Either[E, T.Tuple2[A, B]] {
|
||||
return Apply.SequenceT2(
|
||||
MonadMap[E, A, func(B) T.Tuple2[A, B]],
|
||||
MonadAp[E, B, T.Tuple2[A, B]],
|
||||
|
||||
a, b,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT3[E, A, B, C any](a Either[E, A], b Either[E, B], c Either[E, C]) Either[E, T.Tuple3[A, B, C]] {
|
||||
return Apply.SequenceT3(
|
||||
MonadMap[E, A, func(B) func(C) T.Tuple3[A, B, C]],
|
||||
MonadAp[E, B, func(C) T.Tuple3[A, B, C]],
|
||||
MonadAp[E, C, T.Tuple3[A, B, C]],
|
||||
|
||||
a, b, c,
|
||||
)
|
||||
}
|
||||
|
||||
func SequenceT4[E, A, B, C, D any](a Either[E, A], b Either[E, B], c Either[E, C], d Either[E, D]) Either[E, T.Tuple4[A, B, C, D]] {
|
||||
return Apply.SequenceT4(
|
||||
MonadMap[E, A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]],
|
||||
MonadAp[E, B, func(C) func(D) T.Tuple4[A, B, C, D]],
|
||||
MonadAp[E, C, func(D) T.Tuple4[A, B, C, D]],
|
||||
MonadAp[E, D, T.Tuple4[A, B, C, D]],
|
||||
|
||||
a, b, c, d,
|
||||
)
|
||||
}
|
60
either/testing/laws.go
Normal file
60
either/testing/laws.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
ET "github.com/ibm/fp-go/either"
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
L "github.com/ibm/fp-go/internal/monad/testing"
|
||||
)
|
||||
|
||||
// AssertLaws asserts the apply monad laws for the `Either` monad
|
||||
func AssertLaws[E, A, B, C any](t *testing.T,
|
||||
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 {
|
||||
|
||||
return L.AssertLaws(t,
|
||||
ET.Eq(eqe, eqa),
|
||||
ET.Eq(eqe, eqb),
|
||||
ET.Eq(eqe, eqc),
|
||||
|
||||
ET.Of[E, A],
|
||||
ET.Of[E, B],
|
||||
ET.Of[E, C],
|
||||
|
||||
ET.Of[E, func(A) A],
|
||||
ET.Of[E, func(A) B],
|
||||
ET.Of[E, func(B) C],
|
||||
ET.Of[E, func(func(A) B) B],
|
||||
|
||||
ET.MonadMap[E, A, A],
|
||||
ET.MonadMap[E, A, B],
|
||||
ET.MonadMap[E, A, C],
|
||||
ET.MonadMap[E, B, C],
|
||||
|
||||
ET.MonadMap[E, func(B) C, func(func(A) B) func(A) C],
|
||||
|
||||
ET.MonadChain[E, A, A],
|
||||
ET.MonadChain[E, A, B],
|
||||
ET.MonadChain[E, A, C],
|
||||
ET.MonadChain[E, B, C],
|
||||
|
||||
ET.MonadAp[E, A, A],
|
||||
ET.MonadAp[E, A, B],
|
||||
ET.MonadAp[E, B, C],
|
||||
ET.MonadAp[E, A, C],
|
||||
|
||||
ET.MonadAp[E, func(A) B, B],
|
||||
ET.MonadAp[E, func(A) B, func(A) C],
|
||||
|
||||
ab,
|
||||
bc,
|
||||
)
|
||||
|
||||
}
|
33
either/testing/laws_test.go
Normal file
33
either/testing/laws_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
EQ "github.com/ibm/fp-go/eq"
|
||||
"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]()
|
||||
|
||||
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, eqe, eqa, eqb, eqc, ab, bc)
|
||||
|
||||
assert.True(t, laws(true))
|
||||
assert.True(t, laws(false))
|
||||
}
|
54
either/traverse.go
Normal file
54
either/traverse.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
)
|
||||
|
||||
/*
|
||||
*
|
||||
We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
|
||||
|
||||
HKTRB = HKT<Either[B]>
|
||||
HKTA = HKT<A>
|
||||
HKTB = HKT<B>
|
||||
*/
|
||||
func traverse[E, A, B, HKTA, HKTB, HKTRB any](
|
||||
_of func(Either[E, B]) HKTRB,
|
||||
_map func(HKTB, func(B) Either[E, B]) HKTRB,
|
||||
) func(Either[E, A], func(A) HKTB) HKTRB {
|
||||
|
||||
left := F.Flow2(Left[E, B], _of)
|
||||
right := F.Bind2nd(_map, Right[E, B])
|
||||
|
||||
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
|
||||
return fold(ta,
|
||||
left,
|
||||
F.Flow2(f, right),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func Traverse[E, A, B, HKTA, HKTB, HKTRB any](
|
||||
_of func(Either[E, B]) HKTRB,
|
||||
_map func(HKTB, func(B) Either[E, B]) HKTRB,
|
||||
) func(func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||
delegate := traverse[E, A, B, HKTA](_of, _map)
|
||||
return func(f func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||
return F.Bind2nd(delegate, f)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
|
||||
|
||||
HKTRA = HKT<Either[A]>
|
||||
HKTA = HKT<A>
|
||||
HKTB = HKT<B>
|
||||
*/
|
||||
func Sequence[E, A, HKTA, HKTRA any](
|
||||
_of func(Either[E, A]) HKTRA,
|
||||
_map func(HKTA, func(A) Either[E, A]) HKTRA,
|
||||
) func(Either[E, HKTA]) HKTRA {
|
||||
return Fold(F.Flow2(Left[E, A], _of), F.Bind2nd(_map, Right[E, A]))
|
||||
}
|
38
either/traverse_test.go
Normal file
38
either/traverse_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package either
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTraverse(t *testing.T) {
|
||||
f := func(n int) O.Option[int] {
|
||||
if n >= 2 {
|
||||
return O.Of(n)
|
||||
}
|
||||
return O.None[int]()
|
||||
}
|
||||
trav := Traverse[string, int, int, O.Option[Either[string, int]]](
|
||||
O.Of[Either[string, int]],
|
||||
O.MonadMap[int, Either[string, int]],
|
||||
)(f)
|
||||
|
||||
assert.Equal(t, O.Of(Left[string, int]("a")), F.Pipe1(Left[string, int]("a"), trav))
|
||||
assert.Equal(t, O.None[Either[string, int]](), F.Pipe1(Right[string](1), trav))
|
||||
assert.Equal(t, O.Of(Right[string](3)), F.Pipe1(Right[string](3), trav))
|
||||
}
|
||||
|
||||
func TestSequence(t *testing.T) {
|
||||
|
||||
seq := Sequence(
|
||||
O.Of[Either[string, int]],
|
||||
O.MonadMap[int, Either[string, int]],
|
||||
)
|
||||
|
||||
assert.Equal(t, O.Of(Right[string](1)), seq(Right[string](O.Of(1))))
|
||||
assert.Equal(t, O.Of(Left[string, int]("a")), seq(Left[string, O.Option[int]]("a")))
|
||||
assert.Equal(t, O.None[Either[string, int]](), seq(Right[string](O.None[int]())))
|
||||
}
|
81
either/variadic.go
Normal file
81
either/variadic.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package either
|
||||
|
||||
func Variadic0[V, R any](f func([]V) (R, error)) func(...V) Either[error, R] {
|
||||
return func(v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Variadic1[T1, V, R any](f func(T1, []V) (R, error)) func(T1, ...V) Either[error, R] {
|
||||
return func(t1 T1, v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Variadic2[T1, T2, V, R any](f func(T1, T2, []V) (R, error)) func(T1, T2, ...V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Variadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, []V) (R, error)) func(T1, T2, T3, ...V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, t3 T3, v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Variadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, []V) (R, error)) func(T1, T2, T3, T4, ...V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, v ...V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, t4, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic0[V, R any](f func(...V) (R, error)) func([]V) Either[error, R] {
|
||||
return func(v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(v...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic1[T1, V, R any](f func(T1, ...V) (R, error)) func(T1, []V) Either[error, R] {
|
||||
return func(t1 T1, v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, v...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic2[T1, T2, V, R any](f func(T1, T2, ...V) (R, error)) func(T1, T2, []V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, v...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic3[T1, T2, T3, V, R any](f func(T1, T2, T3, ...V) (R, error)) func(T1, T2, T3, []V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, t3 T3, v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, v...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Unvariadic4[T1, T2, T3, T4, V, R any](f func(T1, T2, T3, T4, ...V) (R, error)) func(T1, T2, T3, T4, []V) Either[error, R] {
|
||||
return func(t1 T1, t2 T2, t3 T3, t4 T4, v []V) Either[error, R] {
|
||||
return TryCatchError(func() (R, error) {
|
||||
return f(t1, t2, t3, t4, v...)
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user