1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-23 22:14:53 +02:00
Files
fp-go/v2/semigroup/doc.go

304 lines
8.5 KiB
Go
Raw Normal View History

Implement v2 using type aliases (#141) * fix: initial checkin of v2 Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: slowly migrate IO Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: migrate MonadTraverseArray and TraverseArray Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: migrate traversal Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: complete migration of IO Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: migrate ioeither Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: refactorY Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: next step in migration Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: adjust IO generation code Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: get rid of more IO methods Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: get rid of more IO * fix: convert iooption Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: convert reader Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: convert a bit of reader Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: new build script Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: cleanup Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: reformat Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: simplify Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: some cleanup Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: adjust Pair to Haskell semantic Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: documentation and testcases Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: some performance optimizations Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: remove coverage Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> * fix: better doc Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com> --------- Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-06 09:27:00 +01:00
// 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 semigroup provides implementations of the Semigroup algebraic structure.
# Semigroup
A Semigroup is an algebraic structure consisting of a set together with an associative
binary operation. It extends the Magma structure by adding the associativity law.
Mathematical Definition:
A semigroup is a pair (S, ) where:
- S is a set
- is a binary operation: S × S S
- The operation must be associative: (a b) c = a (b c)
The key difference from Magma is the associativity requirement, which allows operations
to be chained without worrying about parentheses.
# Basic Usage
Creating and using a semigroup:
import (
"fmt"
SG "github.com/IBM/fp-go/v2/semigroup"
)
// Create a semigroup for string concatenation
stringConcat := SG.MakeSemigroup(func(a, b string) string {
return a + b
})
result := stringConcat.Concat("Hello, ", "World!")
fmt.Println(result) // Output: Hello, World!
// Associativity holds
s1 := stringConcat.Concat(stringConcat.Concat("a", "b"), "c")
s2 := stringConcat.Concat("a", stringConcat.Concat("b", "c"))
fmt.Println(s1 == s2) // Output: true
# Built-in Semigroups
The package provides several pre-defined semigroups:
First - Always returns the first argument:
first := SG.First[int]()
result := first.Concat(1, 2) // Returns: 1
Last - Always returns the last argument:
last := SG.Last[int]()
result := last.Concat(1, 2) // Returns: 2
# Semigroup Transformations
Reverse - Swaps the order of arguments:
import N "github.com/IBM/fp-go/v2/number"
sub := SG.MakeSemigroup(func(a, b int) int { return a - b })
reversed := SG.Reverse(sub)
result1 := sub.Concat(10, 3) // 10 - 3 = 7
result2 := reversed.Concat(10, 3) // 3 - 10 = -7
FunctionSemigroup - Lifts a semigroup to work with functions:
import N "github.com/IBM/fp-go/v2/number"
// Semigroup for integers
intSum := N.SemigroupSum[int]()
// Lift to functions that return integers
funcSG := SG.FunctionSemigroup[string](intSum)
f := func(s string) int { return len(s) }
g := func(s string) int { return len(s) * 2 }
// Combine functions
combined := funcSG.Concat(f, g)
result := combined("hello") // len("hello") + len("hello")*2 = 5 + 10 = 15
# Array Operations
ConcatAll - Concatenates all elements in an array with a starting value:
import N "github.com/IBM/fp-go/v2/number"
sum := N.SemigroupSum[int]()
concatAll := SG.ConcatAll(sum)
result := concatAll(10)([]int{1, 2, 3, 4}) // 10 + 1 + 2 + 3 + 4 = 20
MonadConcatAll - Concatenates all elements with a starting value (uncurried):
import N "github.com/IBM/fp-go/v2/number"
sum := N.SemigroupSum[int]()
result := SG.MonadConcatAll(sum)([]int{1, 2, 3, 4}, 10) // 20
GenericConcatAll - Generic version for custom slice types:
type MyInts []int
sum := N.SemigroupSum[int]()
concatAll := SG.GenericConcatAll[MyInts](sum)
result := concatAll(0)(MyInts{1, 2, 3}) // 6
# Higher-Kinded Type Semigroups
ApplySemigroup - Creates a semigroup for applicative functors:
// For a type HKT<A> with map and ap operations
applySG := SG.ApplySemigroup(
fmap, // func(HKT<A>, func(A) func(A) A) HKT<func(A) A>
fap, // func(HKT<func(A) A>, HKT<A>) HKT<A>
baseSemigroup,
)
AltSemigroup - Creates a semigroup for alternative functors:
// For a type HKT<A> with an alt operation
altSG := SG.AltSemigroup(
falt, // func(HKT<A>, func() HKT<A>) HKT<A>
)
# Practical Examples
Example 1: Merging Configurations
type Config struct {
Timeout int
Retries int
}
configSG := SG.MakeSemigroup(func(a, b Config) Config {
return Config{
Timeout: max(a.Timeout, b.Timeout),
Retries: a.Retries + b.Retries,
}
})
default := Config{Timeout: 30, Retries: 3}
user := Config{Timeout: 60, Retries: 5}
override := Config{Timeout: 45, Retries: 2}
// Merge configurations (associative)
final := configSG.Concat(configSG.Concat(default, user), override)
// Result: Config{Timeout: 60, Retries: 10}
Example 2: Combining Validators
type Validator func(string) []string // Returns list of errors
validatorSG := SG.MakeSemigroup(func(v1, v2 Validator) Validator {
return func(s string) []string {
errors1 := v1(s)
errors2 := v2(s)
return append(errors1, errors2...)
}
})
notEmpty := func(s string) []string {
if s == "" {
return []string{"must not be empty"}
}
return nil
}
minLength := func(s string) []string {
if len(s) < 3 {
return []string{"must be at least 3 characters"}
}
return nil
}
// Combine validators
combined := validatorSG.Concat(notEmpty, minLength)
errors := combined("ab") // ["must be at least 3 characters"]
Example 3: Aggregating Statistics
type Stats struct {
Count int
Sum float64
Min float64
Max float64
}
statsSG := SG.MakeSemigroup(func(a, b Stats) Stats {
return Stats{
Count: a.Count + b.Count,
Sum: a.Sum + b.Sum,
Min: min(a.Min, b.Min),
Max: max(a.Max, b.Max),
}
})
s1 := Stats{Count: 3, Sum: 15.0, Min: 2.0, Max: 8.0}
s2 := Stats{Count: 2, Sum: 12.0, Min: 5.0, Max: 7.0}
s3 := Stats{Count: 4, Sum: 20.0, Min: 1.0, Max: 9.0}
// Aggregate statistics (order doesn't matter due to associativity)
total := statsSG.Concat(statsSG.Concat(s1, s2), s3)
// Result: Stats{Count: 9, Sum: 47.0, Min: 1.0, Max: 9.0}
Example 4: Building Query Strings
querySG := SG.MakeSemigroup(func(a, b string) string {
if a == "" {
return b
}
if b == "" {
return a
}
return a + "&" + b
})
base := "api/users"
filter := "status=active"
sort := "sort=name"
page := "page=1"
// Build query string
query := querySG.Concat(querySG.Concat(base+"?"+filter, sort), page)
// Result: "api/users?status=active&sort=name&page=1"
# Relationship to Other Structures
Semigroup extends Magma by adding the associativity law:
- Magma: Has a binary operation
- Semigroup: Has an associative binary operation
- Monoid: Semigroup with an identity element
Converting between structures:
// Semigroup to Magma
magma := SG.ToMagma(semigroup)
// Semigroup to Monoid (requires identity element)
// See the monoid package
# Laws
A valid Semigroup must satisfy the associativity law:
// Associativity: (a • b) • c = a • (b • c)
s.Concat(s.Concat(a, b), c) == s.Concat(a, s.Concat(b, c))
This law ensures that the order of evaluation doesn't matter, allowing for
parallel computation and optimization.
# Function Reference
Core Functions:
- MakeSemigroup[A](func(A, A) A) Semigroup[A] - Creates a semigroup from a binary operation
- Reverse[A](Semigroup[A]) Semigroup[A] - Returns the dual semigroup with swapped arguments
- ToMagma[A](Semigroup[A]) Magma[A] - Converts a semigroup to a magma
Built-in Semigroups:
- First[A]() Semigroup[A] - Always returns the first argument
- Last[A]() Semigroup[A] - Always returns the last argument
Higher-Order Functions:
- FunctionSemigroup[A, B](Semigroup[B]) Semigroup[func(A) B] - Lifts a semigroup to functions
Array Operations:
- ConcatAll[A](Semigroup[A]) func(A) func([]A) A - Concatenates array elements with initial value
- MonadConcatAll[A](Semigroup[A]) func([]A, A) A - Uncurried version of ConcatAll
- GenericConcatAll[GA ~[]A, A](Semigroup[A]) func(A) func(GA) A - Generic version for custom slices
- GenericMonadConcatAll[GA ~[]A, A](Semigroup[A]) func(GA, A) A - Generic uncurried version
Higher-Kinded Type Operations:
- ApplySemigroup[A, HKTA, HKTFA](fmap, fap, Semigroup[A]) Semigroup[HKTA] - Semigroup for applicatives
- AltSemigroup[HKTA, LAZYHKTA](falt) Semigroup[HKTA] - Semigroup for alternatives
# Related Packages
- github.com/IBM/fp-go/v2/magma - Base algebraic structure without associativity
- github.com/IBM/fp-go/v2/monoid - Semigroup with identity element
- github.com/IBM/fp-go/v2/number - Numeric semigroups (sum, product, min, max)
- github.com/IBM/fp-go/v2/string - String semigroups
- github.com/IBM/fp-go/v2/array - Array operations using semigroups
- github.com/IBM/fp-go/v2/function - Function composition utilities
*/
package semigroup