1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-23 22:14:53 +02:00
Files
fp-go/v2/option/option.go
Dr. Carsten Leue 13063bbd88 fix: doc and tests
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-11 16:36:12 +01:00

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)
}