1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-23 22:14:53 +02:00
Files
fp-go/v2/idiomatic/option/doc.go
Dr. Carsten Leue 03d9720a29 fix: optimize performance for option
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-17 12:19:24 +01:00

237 lines
7.1 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 using idiomatic Go tuple signatures.
//
// Unlike the standard option package which uses wrapper structs, this package represents
// Options as tuples (value, bool) where the boolean indicates presence (true) or absence (false).
// This approach is more idiomatic in Go and has better performance characteristics.
//
// # Type Signatures
//
// The core types used in this package are:
//
// Operator[A, B any] = func(A, bool) (B, bool) // Transforms an Option[A] to Option[B]
// Kleisli[A, B any] = func(A) (B, bool) // Monadic function from A to Option[B]
//
// # Basic Usage
//
// Create an Option with Some or None:
//
// some := Some(42) // (42, true)
// none := None[int]() // (0, false)
// opt := Of(42) // Alternative to Some: (42, true)
//
// Check if an Option contains a value:
//
// value, ok := Some(42)
// if ok {
// // value == 42
// }
//
// if IsSome(Some(42)) {
// // Option contains a value
// }
// if IsNone(None[int]()) {
// // Option is empty
// }
//
// Extract values:
//
// value, ok := Some(42) // Direct tuple unpacking: value == 42, ok == true
// value := GetOrElse(func() int { return 0 })(Some(42)) // Returns 42
// value := GetOrElse(func() int { return 0 })(None[int]()) // Returns 0
//
// # Transformations
//
// Map transforms the contained value:
//
// double := Map(func(x int) int { return x * 2 })
// result := double(Some(21)) // (42, true)
// result := double(None[int]()) // (0, false)
//
// Chain sequences operations that may fail:
//
// validate := Chain(func(x int) (int, bool) {
// if x > 0 { return x * 2, true }
// return 0, false
// })
// result := validate(Some(5)) // (10, true)
// result := validate(Some(-1)) // (0, false)
//
// Filter keeps values that satisfy a predicate:
//
// isPositive := Filter(func(x int) bool { return x > 0 })
// result := isPositive(Some(5)) // (5, true)
// result := isPositive(Some(-1)) // (0, false)
//
// # Working with Collections
//
// Transform arrays using TraverseArray:
//
// doublePositive := func(x int) (int, bool) {
// if x > 0 { return x * 2, true }
// return 0, false
// }
// result := TraverseArray(doublePositive)([]int{1, 2, 3}) // ([2, 4, 6], true)
// result := TraverseArray(doublePositive)([]int{1, -2, 3}) // ([], false)
//
// Transform with indexes:
//
// f := func(i int, x int) (int, bool) {
// if x > i { return x, true }
// return 0, false
// }
// result := TraverseArrayWithIndex(f)([]int{1, 2, 3}) // ([1, 2, 3], true)
//
// Transform records (maps):
//
// double := func(x int) (int, bool) { return x * 2, true }
// result := TraverseRecord(double)(map[string]int{"a": 1, "b": 2})
// // (map[string]int{"a": 2, "b": 4}, true)
//
// # Algebraic Operations
//
// Option supports various algebraic structures:
//
// - Functor: Map operations for transforming values
// - Applicative: Ap operations for applying wrapped functions
// - Monad: Chain operations for sequencing computations
// - Alternative: Alt operations for providing fallbacks
//
// Applicative example:
//
// fab := Some(func(x int) int { return x * 2 })
// fa := Some(21)
// result := Ap[int](fa)(fab) // (42, true)
//
// Alternative example:
//
// withDefault := Alt(func() (int, bool) { return 100, true })
// result := withDefault(Some(42)) // (42, true)
// result := withDefault(None[int]()) // (100, true)
//
// # Conversion Functions
//
// Convert predicates to Options:
//
// isPositive := FromPredicate(func(n int) bool { return n > 0 })
// result := isPositive(5) // (5, true)
// result := isPositive(-1) // (0, false)
//
// Convert nullable pointers to Options:
//
// var ptr *int = nil
// result := FromNillable(ptr) // (nil, false)
// val := 42
// result := FromNillable(&val) // (&val, true)
//
// Convert zero/non-zero values to Options:
//
// result := FromZero[int]()(0) // (0, true)
// result := FromZero[int]()(5) // (0, false)
// result := FromNonZero[int]()(5) // (5, true)
// result := FromNonZero[int]()(0) // (0, false)
//
// Use equality-based conversion:
//
// import "github.com/IBM/fp-go/v2/eq"
// equals42 := FromEq(eq.FromStrictEquals[int]())(42)
// result := equals42(42) // (42, true)
// result := equals42(10) // (0, false)
//
// # Do-Notation Style
//
// Build complex computations using do-notation:
//
// type Result struct {
// x int
// y int
// sum int
// }
//
// result := F.Pipe3(
// Do(Result{}),
// Bind(func(x int) func(Result) Result {
// return func(r Result) Result { r.x = x; return r }
// }, func(r Result) (int, bool) { return Some(10) }),
// Bind(func(y int) func(Result) Result {
// return func(r Result) Result { r.y = y; return r }
// }, func(r Result) (int, bool) { return Some(20) }),
// Let(func(sum int) func(Result) Result {
// return func(r Result) Result { r.sum = sum; return r }
// }, func(r Result) int { return r.x + r.y }),
// ) // (Result{x: 10, y: 20, sum: 30}, true)
//
// # Lens-Based Operations
//
// Use lenses for cleaner field updates:
//
// type Person struct {
// Name string
// Age int
// }
//
// ageLens := lens.MakeLens(
// func(p Person) int { return p.Age },
// func(p Person, age int) Person { p.Age = age; return p },
// )
//
// // Update using a lens
// incrementAge := BindL(ageLens, func(age int) (int, bool) {
// if age < 120 { return age + 1, true }
// return 0, false
// })
// result := incrementAge(Some(Person{Name: "Alice", Age: 30}))
// // (Person{Name: "Alice", Age: 31}, true)
//
// // Set using a lens
// setAge := LetToL(ageLens, 25)
// result := setAge(Some(Person{Name: "Bob", Age: 30}))
// // (Person{Name: "Bob", Age: 25}, true)
//
// # Folding and Reducing
//
// Fold provides a way to handle both Some and None cases:
//
// 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"
//
// Reduce folds an Option into a single value:
//
// sum := Reduce(func(acc, val int) int { return acc + val }, 0)
// result := sum(Some(5)) // 5
// result := sum(None[int]()) // 0
//
// # Debugging
//
// Convert Options to strings for debugging:
//
// str := ToString(Some(42)) // "Some[int](42)"
// str := ToString(None[int]()) // "None[int]"
//
// # Subpackages
//
// - option/number: Number conversion utilities for working with Options
package option
//go:generate go run .. option --count 10 --filename gen.go
// Made with Bob