From 1dd56f1b2a59a37a59b913b4c5832be171f9d20a Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 17 Apr 2024 13:00:42 -0600 Subject: [PATCH] fn: fix and finish the either API --- fn/either.go | 81 +++++++++++++++++++++++++++++++++++++++++++++++----- fn/option.go | 32 +++++++++++++++++++++ 2 files changed, 106 insertions(+), 7 deletions(-) diff --git a/fn/either.go b/fn/either.go index aedc9fe5b..cb4555f17 100644 --- a/fn/either.go +++ b/fn/either.go @@ -16,6 +16,17 @@ func NewRight[L any, R any](r R) Either[L, R] { return Either[L, R]{left: None[L](), right: Some(r)} } +// ElimEither is the universal Either eliminator. It can be used to safely +// handle all possible values inside the Either by supplying two continuations, +// one for each side of the Either. +func ElimEither[L, R, O any](f func(L) O, g func(R) O, e Either[L, R]) O { + if e.left.IsSome() { + return f(e.left.some) + } + + return g(e.right.some) +} + // WhenLeft executes the given function if the Either is left. func (e Either[L, R]) WhenLeft(f func(L)) { e.left.WhenSome(f) @@ -36,13 +47,69 @@ func (e Either[L, R]) IsRight() bool { return e.right.IsSome() } -// MapLeft maps the left value of the Either to a new value. -func MapLeft[L any, R any, O any](f func(L) O) func(Either[L, R]) Option[O] { - return func(e Either[L, R]) Option[O] { - if e.IsLeft() { - return MapOption(f)(e.left) - } +// LeftToOption converts a Left value to an Option, returning None if the inner +// Either value is a Right value. +func (e Either[L, R]) LeftToOption() Option[L] { + return e.left +} - return None[O]() +// RightToOption converts a Right value to an Option, returning None if the +// inner Either value is a Left value. +func (e Either[L, R]) RightToOption() Option[R] { + return e.right +} + +// UnwrapLeftOr will extract the Left value from the Either if it is present +// returning the supplied default if it is not. +func (e Either[L, R]) UnwrapLeftOr(l L) L { + return e.left.UnwrapOr(l) +} + +// UnwrapRightOr will extract the Right value from the Either if it is present +// returning the supplied default if it is not. +func (e Either[L, R]) UnwrapRightOr(r R) R { + return e.right.UnwrapOr(r) +} + +// Swap reverses the type argument order. This can be useful as an adapter +// between APIs. +func (e Either[L, R]) Swap() Either[R, L] { + return Either[R, L]{ + left: e.right, + right: e.left, + } +} + +// MapLeft maps the left value of the Either to a new value. +func MapLeft[L, R, O any](f func(L) O) func(Either[L, R]) Either[O, R] { + return func(e Either[L, R]) Either[O, R] { + if e.IsLeft() { + return Either[O, R]{ + left: MapOption(f)(e.left), + right: None[R](), + } + } + + return Either[O, R]{ + left: None[O](), + right: e.right, + } + } +} + +// MapRight maps the right value of the Either to a new value. +func MapRight[L, R, O any](f func(R) O) func(Either[L, R]) Either[L, O] { + return func(e Either[L, R]) Either[L, O] { + if e.IsRight() { + return Either[L, O]{ + left: None[L](), + right: MapOption(f)(e.right), + } + } + + return Either[L, O]{ + left: e.left, + right: None[O](), + } } } diff --git a/fn/option.go b/fn/option.go index d500359be..fbd5f2489 100644 --- a/fn/option.go +++ b/fn/option.go @@ -213,3 +213,35 @@ func (o Option[A]) UnsafeFromSome() A { } panic("Option was None()") } + +// OptionToLeft can be used to convert an Option value into an Either, by +// providing the Right value that should be used if the Option value is None. +func OptionToLeft[O, L, R any](o Option[O], r R) Either[O, R] { + if o.IsSome() { + return Either[O, R]{ + left: o, + right: None[R](), + } + } + + return Either[O, R]{ + left: None[O](), + right: Some(r), + } +} + +// OptionToRight can be used to convert an Option value into an Either, by +// providing the Left value that should be used if the Option value is None. +func OptionToRight[O, L, R any](o Option[O], l L) Either[L, O] { + if o.IsSome() { + return Either[L, O]{ + left: None[L](), + right: o, + } + } + + return Either[L, O]{ + left: Some(l), + right: None[O](), + } +}