mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
394 lines
12 KiB
Go
394 lines
12 KiB
Go
// Copyright (c) 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 option implements the Option monad, a data type that can have a defined value or none
|
|
package option
|
|
|
|
import (
|
|
F "github.com/IBM/fp-go/v2/function"
|
|
C "github.com/IBM/fp-go/v2/internal/chain"
|
|
FC "github.com/IBM/fp-go/v2/internal/functor"
|
|
)
|
|
|
|
// fromPredicate creates an Option based on a predicate function.
|
|
// If the predicate returns true for the value, it returns Some(a), otherwise None.
|
|
func fromPredicate[A any](a A, pred func(A) bool) Option[A] {
|
|
if pred(a) {
|
|
return Some(a)
|
|
}
|
|
return None[A]()
|
|
}
|
|
|
|
// FromPredicate returns a function that creates an Option based on a predicate.
|
|
// The returned function will wrap a value in Some if the predicate is satisfied, otherwise None.
|
|
//
|
|
// Example:
|
|
//
|
|
// isPositive := FromPredicate(func(n int) bool { return n > 0 })
|
|
// result := isPositive(5) // Some(5)
|
|
// result := isPositive(-1) // None
|
|
func FromPredicate[A any](pred func(A) bool) Kleisli[A, A] {
|
|
return F.Bind2nd(fromPredicate[A], pred)
|
|
}
|
|
|
|
func FromZero[A comparable]() Kleisli[A, A] {
|
|
var zero A
|
|
return FromPredicate(func(a A) bool { return zero == a })
|
|
}
|
|
|
|
// FromNillable converts a pointer to an Option.
|
|
// Returns Some if the pointer is non-nil, None otherwise.
|
|
//
|
|
// Example:
|
|
//
|
|
// var ptr *int = nil
|
|
// result := FromNillable(ptr) // None
|
|
// val := 42
|
|
// result := FromNillable(&val) // Some(&val)
|
|
func FromNillable[A any](a *A) Option[*A] {
|
|
return fromPredicate(a, F.IsNonNil[A])
|
|
}
|
|
|
|
// FromValidation converts a validation function (returning value and bool) to an Option-returning function.
|
|
// This is an alias for Optionize1.
|
|
//
|
|
// Example:
|
|
//
|
|
// parseNum := FromValidation(func(s string) (int, bool) {
|
|
// n, err := strconv.Atoi(s)
|
|
// return n, err == nil
|
|
// })
|
|
// result := parseNum("42") // Some(42)
|
|
func FromValidation[A, B any](f func(A) (B, bool)) Kleisli[A, B] {
|
|
return Optionize1(f)
|
|
}
|
|
|
|
// MonadAp applies a function wrapped in an Option to a value wrapped in an Option.
|
|
// If either the function or the value is None, returns None.
|
|
// This is the monadic form of the applicative functor.
|
|
//
|
|
// Example:
|
|
//
|
|
// fab := Some(func(x int) int { return x * 2 })
|
|
// fa := Some(5)
|
|
// result := MonadAp(fab, fa) // Some(10)
|
|
func MonadAp[B, A any](fab Option[func(A) B], fa Option[A]) Option[B] {
|
|
return MonadFold(fab, None[B], func(ab func(A) B) Option[B] {
|
|
return MonadFold(fa, None[B], F.Flow2(ab, Some[B]))
|
|
})
|
|
}
|
|
|
|
// Ap is the curried applicative functor for Option.
|
|
// Returns a function that applies an Option-wrapped function to the given Option value.
|
|
//
|
|
// Example:
|
|
//
|
|
// fa := Some(5)
|
|
// applyTo5 := Ap[int](fa)
|
|
// fab := Some(func(x int) int { return x * 2 })
|
|
// result := applyTo5(fab) // Some(10)
|
|
func Ap[B, A any](fa Option[A]) Operator[func(A) B, B] {
|
|
return F.Bind2nd(MonadAp[B, A], fa)
|
|
}
|
|
|
|
// MonadMap applies a function to the value inside an Option.
|
|
// If the Option is None, returns None. This is the monadic form of Map.
|
|
//
|
|
// Example:
|
|
//
|
|
// fa := Some(5)
|
|
// result := MonadMap(fa, func(x int) int { return x * 2 }) // Some(10)
|
|
func MonadMap[A, B any](fa Option[A], f func(A) B) Option[B] {
|
|
return MonadChain(fa, F.Flow2(f, Some[B]))
|
|
}
|
|
|
|
// Map returns a function that applies a transformation to the value inside an Option.
|
|
// If the Option is None, returns None.
|
|
//
|
|
// Example:
|
|
//
|
|
// double := Map(func(x int) int { return x * 2 })
|
|
// result := double(Some(5)) // Some(10)
|
|
// result := double(None[int]()) // None
|
|
func Map[A, B any](f func(a A) B) Operator[A, B] {
|
|
return Chain(F.Flow2(f, Some[B]))
|
|
}
|
|
|
|
// MonadMapTo replaces the value inside an Option with a constant value.
|
|
// If the Option is None, returns None. This is the monadic form of MapTo.
|
|
//
|
|
// Example:
|
|
//
|
|
// fa := Some(5)
|
|
// result := MonadMapTo(fa, "hello") // Some("hello")
|
|
func MonadMapTo[A, B any](fa Option[A], b B) Option[B] {
|
|
return MonadMap(fa, F.Constant1[A](b))
|
|
}
|
|
|
|
// MapTo returns a function that replaces the value inside an Option with a constant.
|
|
//
|
|
// Example:
|
|
//
|
|
// replaceWith42 := MapTo[string, int](42)
|
|
// result := replaceWith42(Some("hello")) // Some(42)
|
|
func MapTo[A, B any](b B) Operator[A, B] {
|
|
return F.Bind2nd(MonadMapTo[A, B], b)
|
|
}
|
|
|
|
// TryCatch executes a function that may return an error and converts the result to an Option.
|
|
// Returns Some(value) if no error occurred, None if an error occurred.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := TryCatch(func() (int, error) {
|
|
// return strconv.Atoi("42")
|
|
// }) // Some(42)
|
|
func TryCatch[A any](f func() (A, error)) Option[A] {
|
|
val, err := f()
|
|
if err != nil {
|
|
return None[A]()
|
|
}
|
|
return Some(val)
|
|
}
|
|
|
|
// Fold provides a way to handle both Some and None cases of an Option.
|
|
// Returns a function that applies onNone if the Option is None, or onSome if it's Some.
|
|
//
|
|
// Example:
|
|
//
|
|
// handler := Fold(
|
|
// func() string { return "no value" },
|
|
// func(x int) string { return fmt.Sprintf("value: %d", x) },
|
|
// )
|
|
// result := handler(Some(42)) // "value: 42"
|
|
// result := handler(None[int]()) // "no value"
|
|
func Fold[A, B any](onNone func() B, onSome func(a A) B) func(ma Option[A]) B {
|
|
return func(ma Option[A]) B {
|
|
return MonadFold(ma, onNone, onSome)
|
|
}
|
|
}
|
|
|
|
// MonadGetOrElse extracts the value from an Option or returns a default value.
|
|
// This is the monadic form of GetOrElse.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := MonadGetOrElse(Some(42), func() int { return 0 }) // 42
|
|
// result := MonadGetOrElse(None[int](), func() int { return 0 }) // 0
|
|
func MonadGetOrElse[A any](fa Option[A], onNone func() A) A {
|
|
return MonadFold(fa, onNone, F.Identity[A])
|
|
}
|
|
|
|
// GetOrElse returns a function that extracts the value from an Option or returns a default.
|
|
//
|
|
// Example:
|
|
//
|
|
// getOrZero := GetOrElse(func() int { return 0 })
|
|
// result := getOrZero(Some(42)) // 42
|
|
// result := getOrZero(None[int]()) // 0
|
|
func GetOrElse[A any](onNone func() A) func(Option[A]) A {
|
|
return Fold(onNone, F.Identity[A])
|
|
}
|
|
|
|
// MonadChain applies a function that returns an Option to the value inside an Option.
|
|
// This is the monadic bind operation. If the input is None, returns None.
|
|
//
|
|
// Example:
|
|
//
|
|
// fa := Some(5)
|
|
// result := MonadChain(fa, func(x int) Option[int] {
|
|
// if x > 0 { return Some(x * 2) }
|
|
// return None[int]()
|
|
// }) // Some(10)
|
|
func MonadChain[A, B any](fa Option[A], f Kleisli[A, B]) Option[B] {
|
|
return MonadFold(fa, None[B], f)
|
|
}
|
|
|
|
// Chain returns a function that applies an Option-returning function to an Option value.
|
|
// This is the curried form of the monadic bind operation.
|
|
//
|
|
// Example:
|
|
//
|
|
// validate := Chain(func(x int) Option[int] {
|
|
// if x > 0 { return Some(x * 2) }
|
|
// return None[int]()
|
|
// })
|
|
// result := validate(Some(5)) // Some(10)
|
|
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
|
return Fold(None[B], f)
|
|
}
|
|
|
|
// MonadChainTo ignores the first Option and returns the second Option.
|
|
// Useful for sequencing operations where the first result is not needed.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := MonadChainTo(Some(5), Some("hello")) // Some("hello")
|
|
func MonadChainTo[A, B any](_ Option[A], mb Option[B]) Option[B] {
|
|
return mb
|
|
}
|
|
|
|
// ChainTo returns a function that ignores its input Option and returns a fixed Option.
|
|
//
|
|
// Example:
|
|
//
|
|
// replaceWith := ChainTo(Some("hello"))
|
|
// result := replaceWith(Some(42)) // Some("hello")
|
|
func ChainTo[A, B any](mb Option[B]) Operator[A, B] {
|
|
return F.Bind2nd(MonadChainTo[A, B], mb)
|
|
}
|
|
|
|
// MonadChainFirst applies a function that returns an Option but keeps the original value.
|
|
// If either operation results in None, returns None.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := MonadChainFirst(Some(5), func(x int) Option[string] {
|
|
// return Some(fmt.Sprintf("%d", x))
|
|
// }) // Some(5) - original value is kept
|
|
func MonadChainFirst[A, B any](ma Option[A], f Kleisli[A, B]) Option[A] {
|
|
return C.MonadChainFirst(
|
|
MonadChain[A, A],
|
|
MonadMap[B, A],
|
|
ma,
|
|
f,
|
|
)
|
|
}
|
|
|
|
// ChainFirst returns a function that applies an Option-returning function but keeps the original value.
|
|
//
|
|
// Example:
|
|
//
|
|
// logAndKeep := ChainFirst(func(x int) Option[string] {
|
|
// fmt.Println(x)
|
|
// return Some("logged")
|
|
// })
|
|
// result := logAndKeep(Some(5)) // Some(5)
|
|
func ChainFirst[A, B any](f Kleisli[A, B]) Kleisli[Option[A], A] {
|
|
return C.ChainFirst(
|
|
Chain[A, A],
|
|
Map[B, A],
|
|
f,
|
|
)
|
|
}
|
|
|
|
// Flatten removes one level of nesting from a nested Option.
|
|
//
|
|
// Example:
|
|
//
|
|
// nested := Some(Some(42))
|
|
// result := Flatten(nested) // Some(42)
|
|
// nested := Some(None[int]())
|
|
// result := Flatten(nested) // None
|
|
func Flatten[A any](mma Option[Option[A]]) Option[A] {
|
|
return MonadChain(mma, F.Identity[Option[A]])
|
|
}
|
|
|
|
// MonadAlt returns the first Option if it's Some, otherwise returns the alternative.
|
|
// This is the monadic form of the Alt operation.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := MonadAlt(Some(5), func() Option[int] { return Some(10) }) // Some(5)
|
|
// result := MonadAlt(None[int](), func() Option[int] { return Some(10) }) // Some(10)
|
|
func MonadAlt[A any](fa Option[A], that func() Option[A]) Option[A] {
|
|
return MonadFold(fa, that, Of[A])
|
|
}
|
|
|
|
// Alt returns a function that provides an alternative Option if the input is None.
|
|
//
|
|
// Example:
|
|
//
|
|
// withDefault := Alt(func() Option[int] { return Some(0) })
|
|
// result := withDefault(Some(5)) // Some(5)
|
|
// result := withDefault(None[int]()) // Some(0)
|
|
func Alt[A any](that func() Option[A]) Kleisli[Option[A], A] {
|
|
return Fold(that, Of[A])
|
|
}
|
|
|
|
// MonadSequence2 sequences two Options and applies a function to their values.
|
|
// Returns None if either Option is None.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := MonadSequence2(Some(2), Some(3), func(a, b int) Option[int] {
|
|
// return Some(a + b)
|
|
// }) // Some(5)
|
|
func MonadSequence2[T1, T2, R any](o1 Option[T1], o2 Option[T2], f func(T1, T2) Option[R]) Option[R] {
|
|
return MonadFold(o1, None[R], func(t1 T1) Option[R] {
|
|
return MonadFold(o2, None[R], func(t2 T2) Option[R] {
|
|
return f(t1, t2)
|
|
})
|
|
})
|
|
}
|
|
|
|
// Sequence2 returns a function that sequences two Options with a combining function.
|
|
//
|
|
// Example:
|
|
//
|
|
// add := Sequence2(func(a, b int) Option[int] { return Some(a + b) })
|
|
// result := add(Some(2), Some(3)) // Some(5)
|
|
func Sequence2[T1, T2, R any](f func(T1, T2) Option[R]) func(Option[T1], Option[T2]) Option[R] {
|
|
return func(o1 Option[T1], o2 Option[T2]) Option[R] {
|
|
return MonadSequence2(o1, o2, f)
|
|
}
|
|
}
|
|
|
|
// Reduce folds an Option into a single value using a reducer function.
|
|
// If the Option is None, returns the initial value.
|
|
//
|
|
// Example:
|
|
//
|
|
// sum := Reduce(func(acc, val int) int { return acc + val }, 0)
|
|
// result := sum(Some(5)) // 5
|
|
// result := sum(None[int]()) // 0
|
|
func Reduce[A, B any](f func(B, A) B, initial B) func(Option[A]) B {
|
|
return Fold(F.Constant(initial), F.Bind1st(f, initial))
|
|
}
|
|
|
|
// Filter keeps the Option if it's Some and the predicate is satisfied, otherwise returns None.
|
|
//
|
|
// Example:
|
|
//
|
|
// isPositive := Filter(func(x int) bool { return x > 0 })
|
|
// result := isPositive(Some(5)) // Some(5)
|
|
// result := isPositive(Some(-1)) // None
|
|
// result := isPositive(None[int]()) // None
|
|
func Filter[A any](pred func(A) bool) Kleisli[Option[A], A] {
|
|
return Fold(None[A], F.Ternary(pred, Of[A], F.Ignore1of1[A](None[A])))
|
|
}
|
|
|
|
// MonadFlap applies a value to a function wrapped in an Option.
|
|
// This is the monadic form of Flap.
|
|
//
|
|
// Example:
|
|
//
|
|
// fab := Some(func(x int) int { return x * 2 })
|
|
// result := MonadFlap(fab, 5) // Some(10)
|
|
func MonadFlap[B, A any](fab Option[func(A) B], a A) Option[B] {
|
|
return FC.MonadFlap(MonadMap[func(A) B, B], fab, a)
|
|
}
|
|
|
|
// Flap returns a function that applies a value to an Option-wrapped function.
|
|
//
|
|
// Example:
|
|
//
|
|
// applyFive := Flap[int](5)
|
|
// fab := Some(func(x int) int { return x * 2 })
|
|
// result := applyFive(fab) // Some(10)
|
|
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
|
return FC.Flap(Map[func(A) B, B], a)
|
|
}
|