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

fix: use tag to discriminate values

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2023-07-08 11:21:26 +02:00
parent c07df5c771
commit c169cb42bb
10 changed files with 173 additions and 262 deletions

65
either/core.go Normal file
View File

@@ -0,0 +1,65 @@
package either
import "fmt"
type EitherTag string
const (
leftTag = "Left"
rightTag = "Right"
)
// 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 `default:"Left"`
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("%s[%T, %T](%v)", s.tag, s.left, s.right, s.left)
case rightTag:
return fmt.Sprintf("%s[%T, %T](%v)", s.tag, s.left, s.right, s.right)
}
return "Invalid"
}
// Format prints some debug info for the object
func (s Either[E, A]) Format(f fmt.State, c rune) {
switch c {
case 's':
fmt.Fprint(f, s.String())
default:
fmt.Fprint(f, s.String())
}
}
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 MonadFold[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
}

View File

@@ -19,8 +19,8 @@ func FromIO[E, A any](f func() A) Either[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]))
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]))
})
}
@@ -33,7 +33,7 @@ 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 fold(fa, F.Flow2(f, Left[E2, B]), F.Flow2(g, Right[E2, B]))
return MonadFold(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.
@@ -50,7 +50,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 fold(fa, F.Flow2(f, Left[B, A]), Right[B, A])
return MonadFold(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] {
@@ -62,7 +62,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 fold(fa, Left[E, B], f)
return MonadFold(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] {
@@ -147,7 +147,7 @@ func FromError[A any](f func(a A) error) func(A) Either[error, A] {
}
func ToError[A any](e Either[error, A]) error {
return fold(e, E.IdentityError, F.Constant1[A, error](nil))
return MonadFold(e, E.IdentityError, F.Constant1[A, error](nil))
}
func Eitherize0G[GA ~func() (R, error), GB ~func() Either[error, R], R any](f GA) GB {
@@ -256,13 +256,9 @@ func Uneitherize4[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) Either[error, R]
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)
return MonadFold(ma, onLeft, onRight)
}
}
@@ -321,17 +317,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 fold(e1, Left[E, R], func(t1 T1) Either[E, R] {
return fold(e2, Left[E, R], func(t2 T2) 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 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 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 f(t1, t2, t3)
})
})
@@ -340,5 +336,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 fold(val, Right[A, E], Left[A, E])
return MonadFold(val, Right[A, E], Left[A, E])
}

View File

@@ -1,55 +0,0 @@
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
}

View File

@@ -1,69 +0,0 @@
//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
}

View File

@@ -14,7 +14,7 @@ func WithResource[E, R, A any](onCreate func() Either[E, R], onRelease func(R) E
res := f(r)
released := onRelease(r)
// handle the errors
return fold(
return MonadFold(
res,
Left[E, A],
func(a A) Either[E, A] {

View File

@@ -21,7 +21,7 @@ func traverse[E, A, B, HKTA, HKTB, HKTRB any](
right := F.Bind2nd(_map, Right[E, B])
return func(ta Either[E, A], f func(A) HKTB) HKTRB {
return fold(ta,
return MonadFold(ta,
left,
F.Flow2(f, right),
)

BIN
fp-go.exe

Binary file not shown.

93
option/core.go Normal file
View File

@@ -0,0 +1,93 @@
package option
import (
"bytes"
"encoding/json"
"fmt"
)
type OptionTag string
const (
noneTag = "None"
someTag = "Some"
)
var (
jsonNull = []byte("null")
)
// Option defines a data structure that logically holds a value or not
type Option[A any] struct {
tag OptionTag `default:"None"`
value A
}
// String prints some debug info for the object
func (s Option[A]) String() string {
switch s.tag {
case noneTag:
return fmt.Sprintf("%s[%T]", s.tag, s.value)
case someTag:
return fmt.Sprintf("%s[%T](%v)", s.tag, s.value, s.value)
}
return "Invalid"
}
// Format prints some debug info for the object
func (s Option[A]) Format(f fmt.State, c rune) {
switch c {
case 's':
fmt.Fprint(f, s.String())
default:
fmt.Fprint(f, s.String())
}
}
func (s Option[A]) MarshalJSON() ([]byte, error) {
if s.tag == noneTag {
return jsonNull, nil
}
return json.Marshal(s.value)
}
func (s Option[A]) UnmarshalJSON(data []byte) error {
// decode the value
if bytes.Equal(data, jsonNull) {
s.tag = noneTag
return nil
}
s.tag = someTag
return json.Unmarshal(data, &s.value)
}
func IsNone[T any](val Option[T]) bool {
return val.tag == noneTag
}
func Some[T any](value T) Option[T] {
return Option[T]{tag: someTag, value: value}
}
func Of[T any](value T) Option[T] {
return Some(value)
}
func None[T any]() Option[T] {
return Option[T]{tag: noneTag}
}
func IsSome[T any](val Option[T]) bool {
return val.tag == someTag
}
func MonadFold[A, B any](ma Option[A], onNone func() B, onSome func(A) B) B {
if IsNone(ma) {
return onNone()
}
return onSome(ma.value)
}
func Unwrap[A any](ma Option[A]) (A, bool) {
return ma.value, ma.tag == someTag
}

View File

@@ -1,58 +0,0 @@
package option
import "fmt"
type OptionTag int
const (
NoneTag OptionTag = iota
SomeTag
)
// Option defines a data structure that logically holds a value or not
type Option[A any] struct {
Tag OptionTag
Value A
}
// String prints some debug info for the object
func (s Option[A]) String() string {
switch s.Tag {
case NoneTag:
return fmt.Sprintf("None[%T]", s.Value)
case SomeTag:
return fmt.Sprintf("Some[%T](%v)", s.Value, s.Value)
}
return "Invalid"
}
func IsNone[T any](val Option[T]) bool {
return val.Tag == NoneTag
}
func Some[T any](value T) Option[T] {
return Option[T]{Tag: SomeTag, Value: value}
}
func Of[T any](value T) Option[T] {
return Some(value)
}
func None[T any]() Option[T] {
return Option[T]{Tag: NoneTag}
}
func IsSome[T any](val Option[T]) bool {
return val.Tag == SomeTag
}
func MonadFold[A, B any](ma Option[A], onNone func() B, onSome func(A) B) B {
if IsNone(ma) {
return onNone()
}
return onSome(ma.Value)
}
func Unwrap[A any](ma Option[A]) (A, bool) {
return ma.Value, ma.Tag == SomeTag
}

View File

@@ -1,61 +0,0 @@
//go:build disabled
package option
// Option will be of type T or None
type Option[T any] interface {
fmt.Stringer
}
type none struct {
}
var const_none = none{}
func (none) String() string {
return "None"
}
type some[T any] struct {
v T
}
func (s some[T]) String() string {
return fmt.Sprintf("Some[%v]", s.v)
}
func IsNone[T any](val Option[T]) bool {
return val == const_none
}
func Some[T any](value T) Option[T] {
return some[T]{v: value}
}
func Of[T any](value T) Option[T] {
return Some(value)
}
func None[T any]() Option[T] {
return const_none
}
func IsSome[T any](val Option[T]) bool {
return !IsNone(val)
}
func MonadFold[A, B any](ma Option[A], onNone func() B, onSome func(A) B) B {
if IsNone(ma) {
return onNone()
}
return onSome(ma.(some[A]).v)
}
func Unwrap[A any](a A) func(Option[A]) (A, bool) {
return func(ma Option[A]) (A, bool) {
if IsNone(ma) {
return a, false
}
return ma.(some[A]).v, true
}
}