mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
fix: refactor either type (#102)
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -47,5 +47,5 @@ func ExampleReadFile() {
|
|||||||
fmt.Println(result())
|
fmt.Println(result())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, string](Carsten)
|
// Right[string](Carsten)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,10 +182,7 @@ func withCancelCauseFunc[
|
|||||||
ma,
|
ma,
|
||||||
IOE.Swap[GIOA, func() E.Either[A, error]],
|
IOE.Swap[GIOA, func() E.Either[A, error]],
|
||||||
IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any {
|
IOE.ChainFirstIOK[func() E.Either[A, error], func() any](func(err error) func() any {
|
||||||
return IO.MakeIO[func() any](func() any {
|
return IO.FromImpure[func() any](func() { cancel(err) })
|
||||||
cancel(err)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
IOE.Swap[func() E.Either[A, error], GIOA],
|
IOE.Swap[func() E.Either[A, error], GIOA],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,17 +23,16 @@ type (
|
|||||||
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
|
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
|
||||||
Either[E, A any] struct {
|
Either[E, A any] struct {
|
||||||
isLeft bool
|
isLeft bool
|
||||||
left E
|
value any
|
||||||
right A
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// String prints some debug info for the object
|
// String prints some debug info for the object
|
||||||
func (s Either[E, A]) String() string {
|
func (s Either[E, A]) String() string {
|
||||||
if s.isLeft {
|
if s.isLeft {
|
||||||
return fmt.Sprintf("Left[%T, %T](%v)", s.left, s.right, s.left)
|
return fmt.Sprintf("Left[%T](%v)", s.value, s.value)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Right[%T, %T](%v)", s.left, s.right, s.right)
|
return fmt.Sprintf("Right[%T](%v)", s.value, s.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format prints some debug info for the object
|
// Format prints some debug info for the object
|
||||||
@@ -58,23 +57,29 @@ func IsRight[E, A any](val Either[E, A]) bool {
|
|||||||
|
|
||||||
// Left creates a new instance of an [Either] representing the left value.
|
// Left creates a new instance of an [Either] representing the left value.
|
||||||
func Left[A, E any](value E) Either[E, A] {
|
func Left[A, E any](value E) Either[E, A] {
|
||||||
return Either[E, A]{isLeft: true, left: value}
|
return Either[E, A]{true, value}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right creates a new instance of an [Either] representing the right value.
|
// Right creates a new instance of an [Either] representing the right value.
|
||||||
func Right[E, A any](value A) Either[E, A] {
|
func Right[E, A any](value A) Either[E, A] {
|
||||||
return Either[E, A]{isLeft: false, right: value}
|
return Either[E, A]{false, value}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case
|
// 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 {
|
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
|
||||||
if ma.isLeft {
|
if ma.isLeft {
|
||||||
return onLeft(ma.left)
|
return onLeft(ma.value.(E))
|
||||||
}
|
}
|
||||||
return onRight(ma.right)
|
return onRight(ma.value.(A))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
|
||||||
return ma.right, ma.left
|
if ma.isLeft {
|
||||||
|
var a A
|
||||||
|
return a, ma.value.(E)
|
||||||
|
} else {
|
||||||
|
var e E
|
||||||
|
return ma.value.(A), e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDefault(t *testing.T) {
|
|
||||||
var e Either[error, string]
|
|
||||||
|
|
||||||
assert.Equal(t, Of[error](""), e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsLeft(t *testing.T) {
|
func TestIsLeft(t *testing.T) {
|
||||||
err := errors.New("Some error")
|
err := errors.New("Some error")
|
||||||
withError := Left[string](err)
|
withError := Left[string](err)
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ func ExampleEither_creation() {
|
|||||||
fmt.Println(rightFromPred)
|
fmt.Println(rightFromPred)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Left[*errors.errorString, string](some error)
|
// Left[*errors.errorString](some error)
|
||||||
// Right[<nil>, string](value)
|
// Right[string](value)
|
||||||
// Left[*errors.errorString, *string](value was nil)
|
// Left[*errors.errorString](value was nil)
|
||||||
// true
|
// true
|
||||||
// Left[*errors.errorString, int](3 is an odd number)
|
// Left[*errors.errorString](3 is an odd number)
|
||||||
// Right[<nil>, int](4)
|
// Right[int](4)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ func ExampleEither_extraction() {
|
|||||||
fmt.Println(doubleFromRightBis)
|
fmt.Println(doubleFromRightBis)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Left[*errors.errorString, int](Division by Zero!)
|
// Left[*errors.errorString](Division by Zero!)
|
||||||
// Right[<nil>, int](10)
|
// Right[int](10)
|
||||||
// 0
|
// 0
|
||||||
// 10
|
// 10
|
||||||
// 0
|
// 0
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ import (
|
|||||||
T "github.com/IBM/fp-go/tuple"
|
T "github.com/IBM/fp-go/tuple"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// undefined represents an undefined value
|
||||||
|
undefined = struct{}{}
|
||||||
|
)
|
||||||
|
|
||||||
// type IO[A any] = func() A
|
// type IO[A any] = func() A
|
||||||
|
|
||||||
func MakeIO[GA ~func() A, A any](f func() A) GA {
|
func MakeIO[GA ~func() A, A any](f func() A) GA {
|
||||||
@@ -43,7 +48,7 @@ func FromIO[GA ~func() A, A any](a GA) GA {
|
|||||||
func FromImpure[GA ~func() any, IMP ~func()](f IMP) GA {
|
func FromImpure[GA ~func() any, IMP ~func()](f IMP) GA {
|
||||||
return MakeIO[GA](func() any {
|
return MakeIO[GA](func() any {
|
||||||
f()
|
f()
|
||||||
return nil
|
return undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func TestLogger(t *testing.T) {
|
|||||||
|
|
||||||
lio := l("out")
|
lio := l("out")
|
||||||
|
|
||||||
assert.Equal(t, nil, lio(10)())
|
assert.NotPanics(t, func() { lio(10)() })
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLogf(t *testing.T) {
|
func TestLogf(t *testing.T) {
|
||||||
@@ -36,5 +36,5 @@ func TestLogf(t *testing.T) {
|
|||||||
|
|
||||||
lio := l("Value is %d")
|
lio := l("Value is %d")
|
||||||
|
|
||||||
assert.Equal(t, nil, lio(10)())
|
assert.NotPanics(t, func() { lio(10)() })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ func ExampleIOEither_creation() {
|
|||||||
fmt.Println(rightFromPred())
|
fmt.Println(rightFromPred())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Left[*errors.errorString, string](some error)
|
// Left[*errors.errorString](some error)
|
||||||
// Right[<nil>, string](value)
|
// Right[string](value)
|
||||||
// Right[<nil>, int](42)
|
// Right[int](42)
|
||||||
// Left[*errors.errorString, int](3 is an odd number)
|
// Left[*errors.errorString](3 is an odd number)
|
||||||
// Right[<nil>, int](4)
|
// Right[int](4)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,5 +53,5 @@ func ExampleIOEither_do() {
|
|||||||
fmt.Println(b())
|
fmt.Println(b())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, int](8)
|
// Right[int](8)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func ExampleIOEither_extraction() {
|
|||||||
fmt.Println(valueFromIO)
|
fmt.Println(valueFromIO)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, int](42)
|
// Right[int](42)
|
||||||
// 42
|
// 42
|
||||||
// 42
|
// 42
|
||||||
|
|
||||||
|
|||||||
@@ -196,8 +196,8 @@ func Example_getAge() {
|
|||||||
fmt.Println(zoltar(MakeUser("2005-12-12")))
|
fmt.Println(zoltar(MakeUser("2005-12-12")))
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, float64](6472)
|
// Right[float64](6472)
|
||||||
// Left[*time.ParseError, float64](parsing time "July 4, 2001" as "2006-01-02": cannot parse "July 4, 2001" as "2006")
|
// Left[*time.ParseError](parsing time "July 4, 2001" as "2006-01-02": cannot parse "July 4, 2001" as "2006")
|
||||||
// If you survive, you will be 6837
|
// If you survive, you will be 6837
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,8 +235,8 @@ func Example_solution08C() {
|
|||||||
fmt.Println(eitherWelcome(theresa08))
|
fmt.Println(eitherWelcome(theresa08))
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Left[*errors.errorString, string](your account is not active)
|
// Left[*errors.errorString](your account is not active)
|
||||||
// Right[<nil>, string](Welcome Theresa)
|
// Right[string](Welcome Theresa)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Example_solution08D() {
|
func Example_solution08D() {
|
||||||
@@ -269,8 +269,8 @@ func Example_solution08D() {
|
|||||||
fmt.Println(register(yi08)())
|
fmt.Println(register(yi08)())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, string](Gary)
|
// Right[string](Gary)
|
||||||
// Left[*errors.errorString, <nil>](Your name Yi is larger than 3 characters)
|
// Left[*errors.errorString](Your name Yi is larger than 3 characters)
|
||||||
// Right[<nil>, string](Welcome Albert)
|
// Right[string](Welcome Albert)
|
||||||
// Left[*errors.errorString, string](Your name Yi is larger than 3 characters)
|
// Left[*errors.errorString](Your name Yi is larger than 3 characters)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,6 @@ func Example_solution09C() {
|
|||||||
fmt.Println(joinMailingList("notanemail")())
|
fmt.Println(joinMailingList("notanemail")())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, string](sleepy@grandpa.net)
|
// Right[string](sleepy@grandpa.net)
|
||||||
// Left[*errors.errorString, string](email notanemail is invalid)
|
// Left[*errors.errorString](email notanemail is invalid)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func Example_renderPage() {
|
|||||||
fmt.Println(res(context.TODO())())
|
fmt.Println(res(context.TODO())())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, string](<div>Destinations: [qui est esse], Events: [ea molestias quasi exercitationem repellat qui ipsa sit aut]</div>)
|
// Right[string](<div>Destinations: [qui est esse], Events: [ea molestias quasi exercitationem repellat qui ipsa sit aut]</div>)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ func Example_solution11B() {
|
|||||||
fmt.Println(findByNameID(4)())
|
fmt.Println(findByNameID(4)())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, string](Albert)
|
// Right[string](Albert)
|
||||||
// Right[<nil>, string](Gary)
|
// Right[string](Gary)
|
||||||
// Right[<nil>, string](Theresa)
|
// Right[string](Theresa)
|
||||||
// Left[*errors.errorString, string](user 4 not found)
|
// Left[*errors.errorString](user 4 not found)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Example_solution11C() {
|
func Example_solution11C() {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func Example_solution12A() {
|
|||||||
fmt.Println(getJsons(routes)())
|
fmt.Println(getJsons(routes)())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, map[string]string](map[/:json for / /about:json for /about])
|
// Right[map[string]string](map[/:json for / /about:json for /about])
|
||||||
}
|
}
|
||||||
|
|
||||||
func Example_solution12B() {
|
func Example_solution12B() {
|
||||||
@@ -74,8 +74,8 @@ func Example_solution12B() {
|
|||||||
fmt.Println(startGame(A.From(playerAlbert, Player{Id: 4})))
|
fmt.Println(startGame(A.From(playerAlbert, Player{Id: 4})))
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, string](Game started)
|
// Right[string](Game started)
|
||||||
// Left[*errors.errorString, string](player 4 must have a name)
|
// Left[*errors.errorString](player 4 must have a name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Example_solution12C() {
|
func Example_solution12C() {
|
||||||
@@ -94,5 +94,5 @@ func Example_solution12C() {
|
|||||||
fmt.Println(readFirst())
|
fmt.Println(readFirst())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, option.Option[string]](Some[string](content of file1 (utf-8)))
|
// Right[option.Option[string]](Some[string](content of file1 (utf-8)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func Example_either_monad() {
|
|||||||
fmt.Println(makeUrl("8080"))
|
fmt.Println(makeUrl("8080"))
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, string](http://localhost:8080)
|
// Right[string](http://localhost:8080)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Example_either_idiomatic() {
|
func Example_either_idiomatic() {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func Example_io_flow() {
|
|||||||
fmt.Println(result())
|
fmt.Println(result())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Right[<nil>, string](Text: Some data, Number: 10)
|
// Right[string](Text: Some data, Number: 10)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user