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

fix: order of parameters on Ap

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2023-07-14 13:20:00 +02:00
parent e42d399509
commit e350f70659
31 changed files with 373 additions and 231 deletions

View File

@@ -6,9 +6,9 @@ import (
)
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)
return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[A, E, 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)
return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[A, E, A], m)
}

View File

@@ -12,8 +12,8 @@ func TestApplySemigroup(t *testing.T) {
sg := ApplySemigroup[string](N.SemigroupSum[int]())
la := Left[string, int]("a")
lb := Left[string, int]("b")
la := Left[int]("a")
lb := Left[int]("b")
r1 := Right[string](1)
r2 := Right[string](2)
r3 := Right[string](3)
@@ -29,8 +29,8 @@ func TestApplicativeMonoid(t *testing.T) {
m := ApplicativeMonoid[string](N.MonoidSum[int]())
la := Left[string, int]("a")
lb := Left[string, int]("b")
la := Left[int]("a")
lb := Left[int]("b")
r1 := Right[string](1)
r2 := Right[string](2)
r3 := Right[string](3)
@@ -44,5 +44,5 @@ func TestApplicativeMonoid(t *testing.T) {
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)})
M.AssertLaws(t, m)([]Either[string, int]{Left[int]("a"), Right[string](1)})
}

View File

@@ -1,9 +1,11 @@
package either
import "fmt"
import (
"fmt"
)
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
type (
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
Either[E, A any] struct {
isLeft bool
left E
@@ -29,30 +31,35 @@ func (s Either[E, A]) Format(f fmt.State, c rune) {
}
}
// IsLeft tests if the either is a left value. Rather use [Fold] if you need to access the values. Inverse is [IsRight].
func IsLeft[E, A any](val Either[E, A]) bool {
return val.isLeft
}
// IsLeft tests if the either is a right value. Rather use [Fold] if you need to access the values. Inverse is [IsLeft].
func IsRight[E, A any](val Either[E, A]) bool {
return !val.isLeft
}
func Left[E, A any](value E) Either[E, A] {
// Left creates a new instance of an [Either] representing the left value.
func Left[A, E any](value E) Either[E, A] {
return Either[E, A]{isLeft: true, left: value}
}
// Right creates a new instance of an [Either] representing the right value.
func Right[E, A any](value A) Either[E, A] {
return Either[E, A]{isLeft: false, right: value}
}
// MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
if IsLeft(ma) {
if ma.isLeft {
return onLeft(ma.left)
}
return onRight(ma.right)
}
// Unwrap converts an Either into the idiomatic tuple
// Unwrap converts an [Either] into the idiomatic tuple
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
return ma.right, ma.left
}

View File

@@ -10,22 +10,23 @@ import (
O "github.com/ibm/fp-go/option"
)
// Of is equivalent to [Right]
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] {
func FromIO[E, IO ~func() A, A any](f IO) 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 MonadFold(fab, Left[E, B], func(ab func(A) B) Either[E, B] {
return MonadFold(fa, Left[E, B], F.Flow2(ab, Right[E, B]))
func MonadAp[B, E, A any](fab Either[E, func(a A) B], fa Either[E, A]) Either[E, B] {
return MonadFold(fab, Left[B, E], func(ab func(A) B) Either[E, B] {
return MonadFold(fa, Left[B, E], 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 Ap[B, E, A any](fa Either[E, A]) func(fab Either[E, func(a A) B]) Either[E, B] {
return F.Bind2nd(MonadAp[B, E, A], fa)
}
func MonadMap[E, A, B any](fa Either[E, A], f func(a A) B) Either[E, B] {
@@ -33,12 +34,12 @@ func MonadMap[E, A, B any](fa Either[E, A], f func(a A) B) Either[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 MonadFold(fa, F.Flow2(f, Left[E2, B]), F.Flow2(g, Right[E2, B]))
return MonadFold(fa, F.Flow2(f, Left[B, E2]), 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]))
return Fold(F.Flow2(f, Left[B, E2]), F.Flow2(g, Right[E2, B]))
}
func MonadMapTo[E, A, B any](fa Either[E, A], b B) Either[E, B] {
@@ -50,7 +51,7 @@ func MapTo[E, A, B any](b B) func(Either[E, A]) Either[E, B] {
}
func MonadMapLeft[E, A, B any](fa Either[E, A], f func(E) B) Either[B, A] {
return MonadFold(fa, F.Flow2(f, Left[B, A]), Right[B, A])
return MonadFold(fa, F.Flow2(f, Left[A, B]), Right[B, A])
}
func Map[E, A, B any](f func(a A) B) func(fa Either[E, A]) Either[E, B] {
@@ -62,7 +63,7 @@ func MapLeft[E, A, B any](f func(E) B) func(fa Either[E, A]) Either[B, A] {
}
func MonadChain[E, A, B any](fa Either[E, A], f func(a A) Either[E, B]) Either[E, B] {
return MonadFold(fa, Left[E, B], f)
return MonadFold(fa, Left[B, E], f)
}
func MonadChainFirst[E, A, B any](ma Either[E, A], f func(a A) Either[E, B]) Either[E, A] {
@@ -105,7 +106,7 @@ func Flatten[E, A any](mma Either[E, Either[E, A]]) Either[E, A] {
func TryCatch[FA ~func() (A, error), FE func(error) E, E, A any](f FA, onThrow FE) Either[E, A] {
val, err := f()
if err != nil {
return F.Pipe2(err, onThrow, Left[E, A])
return F.Pipe2(err, onThrow, Left[A, E])
}
return F.Pipe1(val, Right[E, A])
}
@@ -127,7 +128,7 @@ func Sequence3[E, T1, T2, T3, R any](f func(T1, T2, T3) Either[E, R]) func(Eithe
}
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])
return O.Fold(F.Nullary2(onNone, Left[A, E]), Right[E, A])
}
func ToOption[E, A any]() func(Either[E, A]) O.Option[A] {
@@ -162,7 +163,7 @@ func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Eithe
if pred(a) {
return Right[E](a)
}
return Left[E, A](onFalse(a))
return Left[A, E](onFalse(a))
}
}
@@ -198,7 +199,7 @@ func ToType[E, A any](onError func(any) E) func(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]),
O.Fold(F.Nullary3(F.Constant(value), onError, Left[A, E]), Right[E, A]),
)
}
}
@@ -208,17 +209,17 @@ func Memoize[E, A any](val Either[E, A]) Either[E, A] {
}
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 MonadFold(e1, Left[E, R], func(t1 T1) Either[E, R] {
return MonadFold(e2, Left[E, R], func(t2 T2) Either[E, R] {
return MonadFold(e1, Left[R, E], func(t1 T1) Either[E, R] {
return MonadFold(e2, Left[R, E], 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 MonadFold(e1, Left[E, R], func(t1 T1) Either[E, R] {
return MonadFold(e2, Left[E, R], func(t2 T2) Either[E, R] {
return MonadFold(e3, Left[E, R], func(t3 T3) Either[E, R] {
return MonadFold(e1, Left[R, E], func(t1 T1) Either[E, R] {
return MonadFold(e2, Left[R, E], func(t2 T2) Either[E, R] {
return MonadFold(e3, Left[R, E], func(t3 T3) Either[E, R] {
return f(t1, t2, t3)
})
})
@@ -227,5 +228,5 @@ func MonadSequence3[E, T1, T2, T3, R any](e1 Either[E, T1], e2 Either[E, T2], e3
// Swap changes the order of type parameters
func Swap[E, A any](val Either[E, A]) Either[A, E] {
return MonadFold(val, Right[A, E], Left[A, E])
return MonadFold(val, Right[A, E], Left[E, A])
}

View File

@@ -19,7 +19,7 @@ func TestDefault(t *testing.T) {
func TestIsLeft(t *testing.T) {
err := errors.New("Some error")
withError := Left[error, string](err)
withError := Left[string](err)
assert.True(t, IsLeft(withError))
assert.False(t, IsRight(withError))
@@ -37,7 +37,7 @@ 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")
exp2 := Left[int]("s")
assert.Equal(t, val2, exp2)
}
@@ -45,7 +45,7 @@ func TestMapEither(t *testing.T) {
func TestUnwrapError(t *testing.T) {
a := ""
err := errors.New("Some error")
withError := Left[error, string](err)
withError := Left[string](err)
res, extracted := UnwrapError(withError)
assert.Equal(t, a, res)
@@ -65,16 +65,16 @@ func TestReduce(t *testing.T) {
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"))))
assert.Equal(t, Right[string](3), F.Pipe1(Right[string](f), Ap[int, string, string](Right[string]("abc"))))
assert.Equal(t, Left[int]("maError"), F.Pipe1(Right[string](f), Ap[int, string, string](Left[string, string]("maError"))))
assert.Equal(t, Left[int]("mabError"), F.Pipe1(Left[func(string) int]("mabError"), Ap[int, string, string](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")))))
assert.Equal(t, Right[string](1), F.Pipe1(Right[string](1), Alt(F.Constant(Left[int]("a")))))
assert.Equal(t, Right[string](2), F.Pipe1(Left[int]("b"), Alt(F.Constant(Right[string](2)))))
assert.Equal(t, Left[int]("b"), F.Pipe1(Left[int]("a"), Alt(F.Constant(Left[int]("b")))))
}
func TestChainFirst(t *testing.T) {
@@ -92,11 +92,11 @@ func TestChainOptionK(t *testing.T) {
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")))
assert.Equal(t, Left[int]("a"), f(Right[string](-1)))
assert.Equal(t, Left[int]("b"), f(Left[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, Left[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)))
}

View File

@@ -1,6 +1,6 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at
// 2023-07-13 22:33:52.2075869 +0200 CEST m=+0.011616301
// 2023-07-14 13:19:40.5850892 +0200 CEST m=+0.008180901
package either

View File

@@ -12,9 +12,9 @@ func TestEq(t *testing.T) {
r2 := Of[string](1)
r3 := Of[string](2)
e1 := Left[string, int]("a")
e2 := Left[string, int]("a")
e3 := Left[string, int]("b")
e1 := Left[int]("a")
e2 := Left[int]("a")
e3 := Left[int]("b")
eq := FromStrictEquals[string, int]()

View File

@@ -11,7 +11,7 @@ func _log[E, A any](left func(string, ...any), right func(string, ...any), prefi
return Fold(
func(e E) Either[E, A] {
left("%s: %v", prefix, e)
return Left[E, A](e)
return Left[A, E](e)
},
func(a A) Either[E, A] {
right("%s: %v", prefix, a)

View File

@@ -10,7 +10,7 @@ func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func
return RR.Traverse[GA](
Of[E, GB],
MonadMap[E, GB, func(B) GB],
MonadAp[E, B, GB],
MonadAp[GB, E, B],
f,
)
}

View File

@@ -16,7 +16,7 @@ func WithResource[E, R, A any](onCreate func() Either[E, R], onRelease func(R) E
// handle the errors
return MonadFold(
res,
Left[E, A],
Left[A, E],
func(a A) Either[E, A] {
return F.Pipe1(
released,

View File

@@ -9,15 +9,15 @@ import (
func SequenceT1[E, A any](a Either[E, A]) Either[E, T.Tuple1[A]] {
return Apply.SequenceT1(
MonadMap[E, A, T.Tuple1[A]],
Map[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]],
Map[E, A, func(B) T.Tuple2[A, B]],
Ap[T.Tuple2[A, B], E, B],
a, b,
)
@@ -25,9 +25,9 @@ func SequenceT2[E, A, B any](a Either[E, A], b Either[E, B]) Either[E, T.Tuple2[
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]],
Map[E, A, func(B) func(C) T.Tuple3[A, B, C]],
Ap[func(C) T.Tuple3[A, B, C], E, B],
Ap[T.Tuple3[A, B, C], E, C],
a, b, c,
)
@@ -35,10 +35,10 @@ func SequenceT3[E, A, B, C any](a Either[E, A], b Either[E, B], c Either[E, 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]],
Map[E, A, func(B) func(C) func(D) T.Tuple4[A, B, C, D]],
Ap[func(C) func(D) T.Tuple4[A, B, C, D], E, B],
Ap[func(D) T.Tuple4[A, B, C, D], E, C],
Ap[T.Tuple4[A, B, C, D], E, D],
a, b, c, d,
)

View File

@@ -45,13 +45,13 @@ func AssertLaws[E, A, B, C any](t *testing.T,
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[A, E, A],
ET.MonadAp[B, E, A],
ET.MonadAp[C, E, B],
ET.MonadAp[C, E, A],
ET.MonadAp[E, func(A) B, B],
ET.MonadAp[E, func(A) B, func(A) C],
ET.MonadAp[B, E, func(A) B],
ET.MonadAp[func(A) C, E, func(A) B],
ab,
bc,

View File

@@ -17,7 +17,7 @@ func traverse[E, A, B, HKTA, HKTB, HKTRB any](
_map func(HKTB, func(B) Either[E, B]) HKTRB,
) func(Either[E, A], func(A) HKTB) HKTRB {
left := F.Flow2(Left[E, B], _of)
left := F.Flow2(Left[B, E], _of)
right := F.Bind2nd(_map, Right[E, B])
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
@@ -50,5 +50,5 @@ 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]))
return Fold(F.Flow2(Left[A, E], _of), F.Bind2nd(_map, Right[E, A]))
}

View File

@@ -20,7 +20,7 @@ func TestTraverse(t *testing.T) {
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.Of(Left[int]("a")), F.Pipe1(Left[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))
}
@@ -33,6 +33,6 @@ func TestSequence(t *testing.T) {
)
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.Of(Left[int]("a")), seq(Left[O.Option[int]]("a")))
assert.Equal(t, O.None[Either[string, int]](), seq(Right[string](O.None[int]())))
}