mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
557 lines
16 KiB
Go
557 lines
16 KiB
Go
// Copyright (c) 2023 - 2025 IBM Corp.
|
|
// All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// package result implements the Either monad
|
|
//
|
|
// A data type that can be of either of two types but not both. This is
|
|
// typically used to carry an error or a return value
|
|
package result
|
|
|
|
import (
|
|
"github.com/IBM/fp-go/v2/either"
|
|
"github.com/IBM/fp-go/v2/option"
|
|
)
|
|
|
|
// Of constructs a Right value containing the given value.
|
|
// This is the monadic return/pure operation for Either.
|
|
// Equivalent to [Right].
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.Of[error](42) // Right(42)
|
|
//
|
|
//go:inline
|
|
func Of[A any](value A) Result[A] {
|
|
return either.Of[error](value)
|
|
}
|
|
|
|
// FromIO executes an IO operation and wraps the result in a Right value.
|
|
// This is useful for lifting pure IO operations into the Either context.
|
|
//
|
|
// Example:
|
|
//
|
|
// getValue := func() int { return 42 }
|
|
// result := either.FromIO[error](getValue) // Right(42)
|
|
//
|
|
//go:inline
|
|
func FromIO[IO ~func() A, A any](f IO) Result[A] {
|
|
return either.FromIO[error](f)
|
|
}
|
|
|
|
// MonadAp applies a function wrapped in Either to a value wrapped in Either.
|
|
// If either the function or the value is Left, returns Left.
|
|
// This is the applicative apply operation.
|
|
//
|
|
// Example:
|
|
//
|
|
// fab := either.Right[error](N.Mul(2))
|
|
// fa := either.Right[error](21)
|
|
// result := either.MonadAp(fab, fa) // Right(42)
|
|
//
|
|
//go:inline
|
|
func MonadAp[B, A any](fab Result[func(a A) B], fa Result[A]) Result[B] {
|
|
return either.MonadAp(fab, fa)
|
|
}
|
|
|
|
// Ap is the curried version of [MonadAp].
|
|
// Returns a function that applies a wrapped function to the given wrapped value.
|
|
//
|
|
//go:inline
|
|
func Ap[B, A any](fa Result[A]) Operator[func(A) B, B] {
|
|
return either.Ap[B](fa)
|
|
}
|
|
|
|
// MonadMap transforms the Right value using the provided function.
|
|
// If the Either is Left, returns Left unchanged.
|
|
// This is the functor map operation.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.MonadMap(
|
|
// either.Right[error](21),
|
|
// N.Mul(2),
|
|
// ) // Right(42)
|
|
//
|
|
//go:inline
|
|
func MonadMap[A, B any](fa Result[A], f func(a A) B) Result[B] {
|
|
return either.MonadMap(fa, f)
|
|
}
|
|
|
|
// MonadBiMap applies two functions: one to transform a Left value, another to transform a Right value.
|
|
// This allows transforming both channels of the Either simultaneously.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.MonadBiMap(
|
|
// either.Left[int](errors.New("error")),
|
|
// error.Error,
|
|
// func(n int) string { return fmt.Sprint(n) },
|
|
// ) // Left("error")
|
|
//
|
|
//go:inline
|
|
func MonadBiMap[E, A, B any](fa Result[A], f func(error) E, g func(a A) B) Either[E, B] {
|
|
return either.MonadBiMap(fa, f, g)
|
|
}
|
|
|
|
// BiMap is the curried version of [MonadBiMap].
|
|
// Maps a pair of functions over the two type arguments of the bifunctor.
|
|
//
|
|
//go:inline
|
|
func BiMap[E, A, B any](f func(error) E, g func(a A) B) func(Result[A]) Either[E, B] {
|
|
return either.BiMap(f, g)
|
|
}
|
|
|
|
// MonadMapTo replaces the Right value with a constant value.
|
|
// If the Either is Left, returns Left unchanged.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.MonadMapTo(either.Right[error](21), "success") // Right("success")
|
|
//
|
|
//go:inline
|
|
func MonadMapTo[A, B any](fa Result[A], b B) Result[B] {
|
|
return either.MonadMapTo(fa, b)
|
|
}
|
|
|
|
// MapTo is the curried version of [MonadMapTo].
|
|
//
|
|
//go:inline
|
|
func MapTo[A, B any](b B) Operator[A, B] {
|
|
return either.MapTo[error, A](b)
|
|
}
|
|
|
|
// MonadMapLeft applies a transformation function to the Left (error) value.
|
|
// If the Either is Right, returns Right unchanged.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.MonadMapLeft(
|
|
// either.Left[int](errors.New("error")),
|
|
// error.Error,
|
|
// ) // Left("error")
|
|
//
|
|
//go:inline
|
|
func MonadMapLeft[A, E any](fa Result[A], f func(error) E) Either[E, A] {
|
|
return either.MonadMapLeft(fa, f)
|
|
}
|
|
|
|
// Map is the curried version of [MonadMap].
|
|
// Transforms the Right value using the provided function.
|
|
//
|
|
//go:inline
|
|
func Map[A, B any](f func(a A) B) Operator[A, B] {
|
|
return either.Map[error](f)
|
|
}
|
|
|
|
// MapLeft is the curried version of [MonadMapLeft].
|
|
// Applies a mapping function to the Left (error) channel.
|
|
//
|
|
//go:inline
|
|
func MapLeft[A, E any](f func(error) E) func(fa Result[A]) Either[E, A] {
|
|
return either.MapLeft[A](f)
|
|
}
|
|
|
|
// MonadChain sequences two computations, where the second depends on the result of the first.
|
|
// If the first Either is Left, returns Left without executing the second computation.
|
|
// This is the monadic bind operation (also known as flatMap).
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.MonadChain(
|
|
// either.Right[error](21),
|
|
// func(x int) either.Result[int] {
|
|
// return either.Right[error](x * 2)
|
|
// },
|
|
// ) // Right(42)
|
|
//
|
|
//go:inline
|
|
func MonadChain[A, B any](fa Result[A], f Kleisli[A, B]) Result[B] {
|
|
return either.MonadChain(fa, f)
|
|
}
|
|
|
|
// MonadChainFirst executes a side-effect computation but returns the original value.
|
|
// Useful for performing actions (like logging) without changing the value.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.MonadChainFirst(
|
|
// either.Right[error](42),
|
|
// func(x int) either.Result[string] {
|
|
// fmt.Println(x) // side effect
|
|
// return either.Right[error]("logged")
|
|
// },
|
|
// ) // Right(42) - original value preserved
|
|
//
|
|
//go:inline
|
|
func MonadChainFirst[A, B any](ma Result[A], f Kleisli[A, B]) Result[A] {
|
|
return either.MonadChainFirst(ma, f)
|
|
}
|
|
|
|
// MonadChainTo ignores the first Either and returns the second.
|
|
// Useful for sequencing operations where you don't need the first result.
|
|
//
|
|
//go:inline
|
|
func MonadChainTo[A, B any](ma Result[A], mb Result[B]) Result[B] {
|
|
return either.MonadChainTo(ma, mb)
|
|
}
|
|
|
|
// MonadChainOptionK chains a function that returns an Option, converting None to Left.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.MonadChainOptionK(
|
|
// func() error { return errors.New("not found") },
|
|
// either.Right[error](42),
|
|
// func(x int) option.Option[string] {
|
|
// if x > 0 { return option.Some("positive") }
|
|
// return option.None[string]()
|
|
// },
|
|
// ) // Right("positive")
|
|
//
|
|
//go:inline
|
|
func MonadChainOptionK[A, B any](onNone func() error, ma Result[A], f option.Kleisli[A, B]) Result[B] {
|
|
return either.MonadChainOptionK(onNone, ma, f)
|
|
}
|
|
|
|
// ChainOptionK is the curried version of [MonadChainOptionK].
|
|
//
|
|
//go:inline
|
|
func ChainOptionK[A, B any](onNone func() error) func(option.Kleisli[A, B]) Operator[A, B] {
|
|
return either.ChainOptionK[A, B](onNone)
|
|
}
|
|
|
|
// ChainTo is the curried version of [MonadChainTo].
|
|
//
|
|
//go:inline
|
|
func ChainTo[A, B any](mb Result[B]) Operator[A, B] {
|
|
return either.ChainTo[A](mb)
|
|
}
|
|
|
|
// Chain is the curried version of [MonadChain].
|
|
// Sequences two computations where the second depends on the first.
|
|
//
|
|
//go:inline
|
|
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
|
return either.Chain(f)
|
|
}
|
|
|
|
// ChainFirst is the curried version of [MonadChainFirst].
|
|
//
|
|
//go:inline
|
|
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
|
|
return either.ChainFirst(f)
|
|
}
|
|
|
|
// Flatten removes one level of nesting from a nested Either.
|
|
//
|
|
// Example:
|
|
//
|
|
// nested := either.Right[error](either.Right[error](42))
|
|
// result := either.Flatten(nested) // Right(42)
|
|
//
|
|
//go:inline
|
|
func Flatten[A any](mma Result[Result[A]]) Result[A] {
|
|
return either.Flatten(mma)
|
|
}
|
|
|
|
// TryCatch converts a (value, error) tuple into an Either, applying a transformation to the error.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.TryCatch(
|
|
// 42, nil,
|
|
// func(err error) string { return err.Error() },
|
|
// ) // Right(42)
|
|
//
|
|
//go:inline
|
|
func TryCatch[FE Endomorphism[error], A any](val A, err error, onThrow FE) Result[A] {
|
|
return either.TryCatch(val, err, onThrow)
|
|
}
|
|
|
|
// TryCatchError is a specialized version of [TryCatch] for error types.
|
|
// Converts a (value, error) tuple into Result[A].
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.TryCatchError(42, nil) // Right(42)
|
|
// result := either.TryCatchError(0, errors.New("fail")) // Left(error)
|
|
//
|
|
//go:inline
|
|
func TryCatchError[A any](val A, err error) Result[A] {
|
|
return either.TryCatchError(val, err)
|
|
}
|
|
|
|
// Sequence2 sequences two Either values using a combining function.
|
|
// Short-circuits on the first Left encountered.
|
|
//
|
|
//go:inline
|
|
func Sequence2[T1, T2, R any](f func(T1, T2) Result[R]) func(Result[T1], Result[T2]) Result[R] {
|
|
return either.Sequence2(f)
|
|
}
|
|
|
|
// Sequence3 sequences three Either values using a combining function.
|
|
// Short-circuits on the first Left encountered.
|
|
//
|
|
//go:inline
|
|
func Sequence3[T1, T2, T3, R any](f func(T1, T2, T3) Result[R]) func(Result[T1], Result[T2], Result[T3]) Result[R] {
|
|
return either.Sequence3(f)
|
|
}
|
|
|
|
// FromOption converts an Option to an Either, using the provided function to generate a Left value for None.
|
|
//
|
|
// Example:
|
|
//
|
|
// opt := option.Some(42)
|
|
// result := either.FromOption[int](func() error { return errors.New("none") })(opt) // Right(42)
|
|
//
|
|
//go:inline
|
|
func FromOption[A any](onNone func() error) func(Option[A]) Result[A] {
|
|
return either.FromOption[A](onNone)
|
|
}
|
|
|
|
// ToOption converts an Either to an Option, discarding the Left value.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.ToOption(either.Right[error](42)) // Some(42)
|
|
// result := either.ToOption(either.Left[int](errors.New("err"))) // None
|
|
//
|
|
//go:inline
|
|
func ToOption[A any](ma Result[A]) Option[A] {
|
|
return either.ToOption(ma)
|
|
}
|
|
|
|
// FromError creates an Either from a function that may return an error.
|
|
//
|
|
// Example:
|
|
//
|
|
// validate := func(x int) error {
|
|
// if x < 0 { return errors.New("negative") }
|
|
// return nil
|
|
// }
|
|
// toEither := either.FromError(validate)
|
|
// result := toEither(42) // Right(42)
|
|
//
|
|
//go:inline
|
|
func FromError[A any](f func(a A) error) Kleisli[A, A] {
|
|
return either.FromError(f)
|
|
}
|
|
|
|
// ToError converts an Result[A] to an error, returning nil for Right values.
|
|
//
|
|
// Example:
|
|
//
|
|
// err := either.ToError(either.Left[int](errors.New("fail"))) // error
|
|
// err := either.ToError(either.Right[error](42)) // nil
|
|
//
|
|
//go:inline
|
|
func ToError[A any](e Result[A]) error {
|
|
return either.ToError(e)
|
|
}
|
|
|
|
// Fold is the curried version of [MonadFold].
|
|
// Extracts the value from an Either by providing handlers for both cases.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.Fold(
|
|
// func(err error) string { return "Error: " + err.Error() },
|
|
// func(n int) string { return fmt.Sprintf("Value: %d", n) },
|
|
// )(either.Right[error](42)) // "Value: 42"
|
|
//
|
|
//go:inline
|
|
func Fold[A, B any](onLeft func(error) B, onRight func(A) B) func(Result[A]) B {
|
|
return either.Fold(onLeft, onRight)
|
|
}
|
|
|
|
// UnwrapError converts an Result[A] into the idiomatic Go tuple (A, error).
|
|
//
|
|
// Example:
|
|
//
|
|
// val, err := either.UnwrapError(either.Right[error](42)) // 42, nil
|
|
// val, err := either.UnwrapError(either.Left[int](errors.New("fail"))) // zero, error
|
|
//
|
|
//go:inline
|
|
func UnwrapError[A any](ma Result[A]) (A, error) {
|
|
return either.UnwrapError(ma)
|
|
}
|
|
|
|
// FromPredicate creates an Either based on a predicate.
|
|
// If the predicate returns true, creates a Right; otherwise creates a Left using onFalse.
|
|
//
|
|
// Example:
|
|
//
|
|
// isPositive := either.FromPredicate(
|
|
// func(x int) bool { return x > 0 },
|
|
// func(x int) error { return errors.New("not positive") },
|
|
// )
|
|
// result := isPositive(42) // Right(42)
|
|
// result := isPositive(-1) // Left(error)
|
|
//
|
|
//go:inline
|
|
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A] {
|
|
return either.FromPredicate(pred, onFalse)
|
|
}
|
|
|
|
// FromNillable creates an Either from a pointer, using the provided error for nil pointers.
|
|
//
|
|
// Example:
|
|
//
|
|
// var ptr *int = nil
|
|
// result := either.FromNillable[int](errors.New("nil"))(ptr) // Left(error)
|
|
// val := 42
|
|
// result := either.FromNillable[int](errors.New("nil"))(&val) // Right(&42)
|
|
//
|
|
//go:inline
|
|
func FromNillable[A any](e error) func(*A) Result[*A] {
|
|
return either.FromNillable[A](e)
|
|
}
|
|
|
|
// GetOrElse extracts the Right value or computes a default from the Left value.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.GetOrElse(func(err error) int { return 0 })(either.Right[error](42)) // 42
|
|
// result := either.GetOrElse(func(err error) int { return 0 })(either.Left[int](err)) // 0
|
|
//
|
|
//go:inline
|
|
func GetOrElse[A any](onLeft func(error) A) func(Result[A]) A {
|
|
return either.GetOrElse(onLeft)
|
|
}
|
|
|
|
// Reduce folds an Either into a single value using a reducer function.
|
|
// Returns the initial value for Left, or applies the reducer to the Right value.
|
|
//
|
|
//go:inline
|
|
func Reduce[A, B any](f func(B, A) B, initial B) func(Result[A]) B {
|
|
return either.Reduce[error](f, initial)
|
|
}
|
|
|
|
// AltW provides an alternative Either if the first is Left, allowing different error types.
|
|
// The 'W' suffix indicates "widening" of the error type.
|
|
//
|
|
// Example:
|
|
//
|
|
// alternative := either.AltW[error, string](func() either.Either[string, int] {
|
|
// return either.Right[string](99)
|
|
// })
|
|
// result := alternative(either.Left[int](errors.New("fail"))) // Right(99)
|
|
//
|
|
//go:inline
|
|
func AltW[E1, A any](that Lazy[Either[E1, A]]) func(Result[A]) Either[E1, A] {
|
|
return either.AltW[error](that)
|
|
}
|
|
|
|
// Alt provides an alternative Either if the first is Left.
|
|
//
|
|
// Example:
|
|
//
|
|
// alternative := either.Alt[error](func() either.Result[int] {
|
|
// return either.Right[error](99)
|
|
// })
|
|
// result := alternative(either.Left[int](errors.New("fail"))) // Right(99)
|
|
//
|
|
//go:inline
|
|
func Alt[A any](that Lazy[Result[A]]) Operator[A, A] {
|
|
return either.Alt(that)
|
|
}
|
|
|
|
// OrElse recovers from a Left by providing an alternative computation.
|
|
//
|
|
// Example:
|
|
//
|
|
// recover := either.OrElse(func(err error) either.Result[int] {
|
|
// return either.Right[error](0) // default value
|
|
// })
|
|
// result := recover(either.Left[int](errors.New("fail"))) // Right(0)
|
|
//
|
|
//go:inline
|
|
func OrElse[A any](onLeft Kleisli[error, A]) Operator[A, A] {
|
|
return either.OrElse(onLeft)
|
|
}
|
|
|
|
// ToType attempts to convert an any value to a specific type, returning Either.
|
|
//
|
|
// Example:
|
|
//
|
|
// convert := either.ToType[int](func(v any) error {
|
|
// return fmt.Errorf("cannot convert %v to int", v)
|
|
// })
|
|
// result := convert(42) // Right(42)
|
|
// result := convert("string") // Left(error)
|
|
//
|
|
//go:inline
|
|
func ToType[A any](onError func(any) error) Kleisli[any, A] {
|
|
return either.ToType[A](onError)
|
|
}
|
|
|
|
// Memoize returns the Either unchanged (Either values are already memoized).
|
|
//
|
|
//go:inline
|
|
func Memoize[A any](val Result[A]) Result[A] {
|
|
return either.Memoize(val)
|
|
}
|
|
|
|
// MonadSequence2 sequences two Either values using a combining function.
|
|
// Short-circuits on the first Left encountered.
|
|
//
|
|
//go:inline
|
|
func MonadSequence2[T1, T2, R any](e1 Result[T1], e2 Result[T2], f func(T1, T2) Result[R]) Result[R] {
|
|
return either.MonadSequence2(e1, e2, f)
|
|
}
|
|
|
|
// MonadSequence3 sequences three Either values using a combining function.
|
|
// Short-circuits on the first Left encountered.
|
|
//
|
|
//go:inline
|
|
func MonadSequence3[T1, T2, T3, R any](e1 Result[T1], e2 Result[T2], e3 Result[T3], f func(T1, T2, T3) Result[R]) Result[R] {
|
|
return either.MonadSequence3(e1, e2, e3, f)
|
|
}
|
|
|
|
// Swap exchanges the Left and Right type parameters.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := either.Swap(either.Right[error](42)) // Left(42)
|
|
// result := either.Swap(either.Left[int](errors.New("err"))) // Right(error)
|
|
//
|
|
//go:inline
|
|
func Swap[A any](val Result[A]) Either[A, error] {
|
|
return either.Swap(val)
|
|
}
|
|
|
|
// MonadFlap applies a value to a function wrapped in Either.
|
|
// This is the reverse of [MonadAp].
|
|
//
|
|
//go:inline
|
|
func MonadFlap[B, A any](fab Result[func(A) B], a A) Result[B] {
|
|
return either.MonadFlap(fab, a)
|
|
}
|
|
|
|
// Flap is the curried version of [MonadFlap].
|
|
//
|
|
//go:inline
|
|
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
|
return either.Flap[error, B](a)
|
|
}
|
|
|
|
// MonadAlt provides an alternative Either if the first is Left.
|
|
// This is the monadic version of [Alt].
|
|
//
|
|
//go:inline
|
|
func MonadAlt[A any](fa Result[A], that Lazy[Result[A]]) Result[A] {
|
|
return either.MonadAlt(fa, that)
|
|
}
|