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:
65
either/core.go
Normal file
65
either/core.go
Normal 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
|
||||
}
|
@@ -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])
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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] {
|
||||
|
@@ -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),
|
||||
)
|
||||
|
93
option/core.go
Normal file
93
option/core.go
Normal 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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user