Documentation
¶
Overview ¶
Package monoid provides the Monoid algebraic structure.
Overview ¶
A Monoid is a Semigroup with an identity element (called Empty). It extends the Magma → Semigroup hierarchy by adding a neutral element that doesn't change other elements when combined with them.
The Monoid interface:
type Monoid[A any] interface {
Semigroup[A] // Provides Concat(x, y A) A
Empty() A // Identity element
}
Monoid laws:
- Associativity (from Semigroup): Concat(Concat(x, y), z) = Concat(x, Concat(y, z))
- Left identity: Concat(Empty(), x) = x
- Right identity: Concat(x, Empty()) = x
Algebraic Hierarchy ¶
Magma (binary operation) ↓ Semigroup (associative) ↓ Monoid (identity element)
Basic Usage ¶
Creating monoids for common types:
// Integer addition monoid (identity: 0)
addMonoid := monoid.MakeMonoid(
func(a, b int) int { return a + b },
0,
)
result := addMonoid.Concat(5, 3) // 8
empty := addMonoid.Empty() // 0
// Verify identity laws
assert.Equal(t, 5, addMonoid.Concat(addMonoid.Empty(), 5)) // Left identity
assert.Equal(t, 5, addMonoid.Concat(5, addMonoid.Empty())) // Right identity
// Integer multiplication monoid (identity: 1)
mulMonoid := monoid.MakeMonoid(
func(a, b int) int { return a * b },
1,
)
result := mulMonoid.Concat(5, 3) // 15
empty := mulMonoid.Empty() // 1
// String concatenation monoid (identity: "")
stringMonoid := monoid.MakeMonoid(
func(a, b string) string { return a + b },
"",
)
result := stringMonoid.Concat("Hello", " World") // "Hello World"
empty := stringMonoid.Empty() // ""
Array Operations ¶
ConcatAll - Combines all elements using the monoid's empty as starting value:
addMonoid := monoid.MakeMonoid(
func(a, b int) int { return a + b },
0,
)
numbers := []int{1, 2, 3, 4, 5}
sum := monoid.ConcatAll(addMonoid)(numbers)
// sum is 15 (0 + 1 + 2 + 3 + 4 + 5)
// Empty slice returns the identity
empty := monoid.ConcatAll(addMonoid)([]int{})
// empty is 0
Fold - Alias for ConcatAll:
mulMonoid := monoid.MakeMonoid(
func(a, b int) int { return a * b },
1,
)
numbers := []int{2, 3, 4}
product := monoid.Fold(mulMonoid)(numbers)
// product is 24 (1 * 2 * 3 * 4)
GenericConcatAll - Works with custom slice types:
type IntSlice []int
addMonoid := monoid.MakeMonoid(
func(a, b int) int { return a + b },
0,
)
numbers := IntSlice{1, 2, 3}
sum := monoid.GenericConcatAll[IntSlice](addMonoid)(numbers)
// sum is 6
Transforming Monoids ¶
Reverse - Swaps the order of arguments:
subMonoid := monoid.MakeMonoid(
func(a, b int) int { return a - b },
0,
)
reversedMonoid := monoid.Reverse(subMonoid)
result1 := subMonoid.Concat(10, 3) // 10 - 3 = 7
result2 := reversedMonoid.Concat(10, 3) // 3 - 10 = -7
ToSemigroup - Converts a Monoid to a Semigroup:
m := monoid.MakeMonoid(
func(a, b int) int { return a + b },
0,
)
sg := monoid.ToSemigroup(m)
result := sg.Concat(5, 3) // 8
Function Monoid ¶
FunctionMonoid - Creates a monoid for functions when the codomain has a monoid:
// Monoid for functions that return integers
intAddMonoid := monoid.MakeMonoid(
func(a, b int) int { return a + b },
0,
)
funcMonoid := monoid.FunctionMonoid[string, int](intAddMonoid)
f1 := func(s string) int { return len(s) }
f2 := func(s string) int { return len(s) * 2 }
// Combine functions: result(x) = f1(x) + f2(x)
combined := funcMonoid.Concat(f1, f2)
result := combined("hello") // len("hello") + len("hello")*2 = 5 + 10 = 15
// Empty function always returns the monoid's empty
emptyFunc := funcMonoid.Empty()
result := emptyFunc("anything") // 0
Applicative Monoid ¶
ApplicativeMonoid - Lifts a monoid into an applicative functor:
// This is used internally for combining applicative effects
// Example with Option-like types:
type Option[A any] struct {
value *A
}
func Some[A any](a A) Option[A] {
return Option[A]{value: &a}
}
func None[A any]() Option[A] {
return Option[A]{value: nil}
}
// Define applicative operations for Option
// Then use ApplicativeMonoid to combine Option values
Alternative Monoid ¶
AltMonoid - Creates a monoid from an Alt type class:
// For types with alternative/fallback semantics // Used with Option, Either, etc. to provide fallback behavior
AlternativeMonoid - Combines applicative and alternative:
// Advanced usage for types that are both Applicative and Alternative // Provides rich composition of effects with fallback
Practical Examples ¶
Boolean AND monoid:
andMonoid := monoid.MakeMonoid(
func(a, b bool) bool { return a && b },
true, // Identity: true AND x = x
)
values := []bool{true, true, true}
result := monoid.ConcatAll(andMonoid)(values) // true
values2 := []bool{true, false, true}
result2 := monoid.ConcatAll(andMonoid)(values2) // false
Boolean OR monoid:
orMonoid := monoid.MakeMonoid(
func(a, b bool) bool { return a || b },
false, // Identity: false OR x = x
)
values := []bool{false, false, false}
result := monoid.ConcatAll(orMonoid)(values) // false
values2 := []bool{false, true, false}
result2 := monoid.ConcatAll(orMonoid)(values2) // true
Max monoid (with bounded integers):
maxMonoid := monoid.MakeMonoid(
func(a, b int) int {
if a > b {
return a
}
return b
},
math.MinInt, // Identity: smallest possible int
)
numbers := []int{3, 7, 2, 9, 1}
maximum := monoid.ConcatAll(maxMonoid)(numbers) // 9
Min monoid (with bounded integers):
minMonoid := monoid.MakeMonoid(
func(a, b int) int {
if a < b {
return a
}
return b
},
math.MaxInt, // Identity: largest possible int
)
numbers := []int{3, 7, 2, 9, 1}
minimum := monoid.ConcatAll(minMonoid)(numbers) // 1
List concatenation monoid:
listMonoid := monoid.MakeMonoid(
func(a, b []int) []int {
result := make([]int, len(a)+len(b))
copy(result, a)
copy(result[len(a):], b)
return result
},
[]int{}, // Identity: empty slice
)
lists := [][]int{{1, 2}, {3, 4}, {5}}
flattened := monoid.ConcatAll(listMonoid)(lists)
// flattened is []int{1, 2, 3, 4, 5}
Monoid Laws ¶
All monoid instances must satisfy these laws:
Associativity (from Semigroup): Concat(Concat(x, y), z) = Concat(x, Concat(y, z))
Left Identity: Concat(Empty(), x) = x
Right Identity: Concat(x, Empty()) = x
Example verification:
m := monoid.MakeMonoid(
func(a, b int) int { return a + b },
0,
)
// Associativity
assert.Equal(t,
m.Concat(m.Concat(1, 2), 3),
m.Concat(1, m.Concat(2, 3)),
) // Both equal 6
// Left identity
assert.Equal(t, 5, m.Concat(m.Empty(), 5))
// Right identity
assert.Equal(t, 5, m.Concat(5, m.Empty()))
Common Monoids ¶
Additive monoid (integers):
- Concat: addition
- Empty: 0
Multiplicative monoid (integers):
- Concat: multiplication
- Empty: 1
String monoid:
- Concat: concatenation
- Empty: ""
List monoid:
- Concat: list concatenation
- Empty: []
Boolean AND monoid:
- Concat: logical AND
- Empty: true
Boolean OR monoid:
- Concat: logical OR
- Empty: false
Functions ¶
Core operations:
- MakeMonoid[A any](func(A, A) A, A) - Create a monoid
- Reverse[A any](Monoid[A]) - Swap argument order
- ToSemigroup[A any](Monoid[A]) - Convert to semigroup
Array operations:
- ConcatAll[A any](Monoid[A]) - Combine all elements
- Fold[A any](Monoid[A]) - Alias for ConcatAll
- GenericConcatAll[GA ~[]A, A any](Monoid[A]) - Generic version
Higher-order:
- FunctionMonoid[A, B any](Monoid[B]) - Monoid for functions
- ApplicativeMonoid[A, HKTA, HKTFA any](...) - Lift into applicative
- AltMonoid[HKTA any, LAZYHKTA ~func() HKTA](...) - From Alt type class
- AlternativeMonoid[A, HKTA, HKTFA any, LAZYHKTA ~func() HKTA](...) - Applicative + Alternative
Related Packages ¶
- semigroup: Parent structure (associative binary operation)
- magma: Grandparent structure (binary operation)
Index ¶
- func ConcatAll[A any](m Monoid[A]) func([]A) A
- func Fold[A any](m Monoid[A]) func([]A) A
- func GenericConcatAll[GA ~[]A, A any](m Monoid[A]) func(GA) A
- func ToSemigroup[A any](m Monoid[A]) S.Semigroup[A]
- type Monoid
- func AltMonoid[HKTA any, LAZYHKTA ~func() HKTA](fzero LAZYHKTA, falt func(HKTA, LAZYHKTA) HKTA) Monoid[HKTA]
- func AlternativeMonoid[A, HKTA, HKTFA any, LAZYHKTA ~func() HKTA](fof func(A) HKTA, fmap func(HKTA, func(A) func(A) A) HKTFA, ...) Monoid[HKTA]
- func ApplicativeMonoid[A, HKTA, HKTFA any](fof func(A) HKTA, fmap func(HKTA, func(A) func(A) A) HKTFA, ...) Monoid[HKTA]
- func FunctionMonoid[A, B any](m Monoid[B]) Monoid[func(A) B]
- func MakeMonoid[A any](c func(A, A) A, e A) Monoid[A]
- func Reverse[A any](m Monoid[A]) Monoid[A]
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ConcatAll ¶
ConcatAll combines all elements in a slice using a monoid.
This function reduces a slice to a single value by repeatedly applying the monoid's Concat operation. For an empty slice, it returns the monoid's Empty value.
This is the standard version that works with []A slices. For custom slice types, use GenericConcatAll.
Parameters:
- m: The monoid to use for combining elements
Returns:
- A function that takes a slice and returns the combined result
Example:
addMonoid := MakeMonoid(
func(a, b int) int { return a + b },
0,
)
concatAll := ConcatAll(addMonoid)
sum := concatAll([]int{1, 2, 3, 4, 5}) // 15
empty := concatAll([]int{}) // 0
stringMonoid := MakeMonoid(
func(a, b string) string { return a + b },
"",
)
concat := ConcatAll(stringMonoid)
result := concat([]string{"Hello", " ", "World"}) // "Hello World"
func Fold ¶
Fold combines all elements in a slice using a monoid.
This is an alias for ConcatAll, providing a more functional programming-style name. It performs a left fold (reduce) operation using the monoid's Concat function, starting with the monoid's Empty value.
Parameters:
- m: The monoid to use for combining elements
Returns:
- A function that takes a slice and returns the combined result
Example:
mulMonoid := MakeMonoid(
func(a, b int) int { return a * b },
1,
)
fold := Fold(mulMonoid)
product := fold([]int{2, 3, 4}) // 24 (1 * 2 * 3 * 4)
empty := fold([]int{}) // 1
func GenericConcatAll ¶
GenericConcatAll combines all elements in a generic slice using a monoid.
This function works with custom slice types (types that are defined as ~[]A). It uses the monoid's Concat operation to combine elements and returns the monoid's Empty value for empty slices.
Type Parameters:
- GA: A generic slice type constraint (~[]A)
- A: The element type
Parameters:
- m: The monoid to use for combining elements
Returns:
- A function that takes a slice and returns the combined result
Example:
type IntSlice []int
addMonoid := MakeMonoid(
func(a, b int) int { return a + b },
0,
)
concatAll := GenericConcatAll[IntSlice](addMonoid)
result := concatAll(IntSlice{1, 2, 3, 4, 5}) // 15
empty := concatAll(IntSlice{}) // 0
func ToSemigroup ¶
ToSemigroup converts a Monoid to a Semigroup by discarding the identity element.
This is useful when you need to use a monoid in a context that only requires a semigroup (associative binary operation without identity).
Parameters:
- m: The monoid to convert
Returns:
- A Semigroup[A] that uses the same Concat operation
Example:
addMonoid := MakeMonoid(
func(a, b int) int { return a + b },
0,
)
sg := ToSemigroup(addMonoid)
result := sg.Concat(5, 3) // 8 (identity not available)
Types ¶
type Monoid ¶
Monoid represents an algebraic structure with an associative binary operation and an identity element.
A Monoid extends Semigroup by adding an identity element (Empty) that satisfies:
- Left identity: Concat(Empty(), x) = x
- Right identity: Concat(x, Empty()) = x
The Monoid must also satisfy the associativity law from Semigroup:
- Associativity: Concat(Concat(x, y), z) = Concat(x, Concat(y, z))
Common examples:
- Integer addition with 0 as identity
- Integer multiplication with 1 as identity
- String concatenation with "" as identity
- List concatenation with [] as identity
- Boolean AND with true as identity
- Boolean OR with false as identity
func AltMonoid ¶
func AltMonoid[HKTA any, LAZYHKTA ~func() HKTA]( fzero LAZYHKTA, falt func(HKTA, LAZYHKTA) HKTA, ) Monoid[HKTA]
AltMonoid creates a monoid from an Alt type class (alternative/choice operation).
This creates a monoid for types that support alternative/fallback semantics, where the Concat operation tries the first value and falls back to the second if the first fails or is empty. The Empty value is provided by fzero.
This is commonly used with Option, Either, Parser, and similar types that represent computations that may fail or have multiple alternatives.
Type Parameters:
- HKTA: The higher-kinded type (e.g., Option[A], Either[E, A])
- LAZYHKTA: A lazy/deferred computation of HKTA (typically func() HKTA)
Parameters:
- fzero: A lazy computation that produces the empty/zero value
- falt: The alternative operation that provides fallback behavior
Returns:
- A Monoid[HKTA] with alternative/choice semantics
Example (conceptual with Option-like type):
optMonoid := AltMonoid(
func() Option[int] { return None() }, // empty
func(first Option[int], second func() Option[int]) Option[int] {
if first.IsSome() {
return first
}
return second()
},
)
// First Some wins
result1 := optMonoid.Concat(Some(5), Some(3)) // Some(5)
// Falls back to second when first is None
result2 := optMonoid.Concat(None(), Some(3)) // Some(3)
// Both None returns None
result3 := optMonoid.Concat(None(), None()) // None()
func AlternativeMonoid ¶
func AlternativeMonoid[A, HKTA, HKTFA any, LAZYHKTA ~func() HKTA]( fof func(A) HKTA, fmap func(HKTA, func(A) func(A) A) HKTFA, fap func(HKTFA, HKTA) HKTA, falt func(HKTA, LAZYHKTA) HKTA, m Monoid[A], ) Monoid[HKTA]
AlternativeMonoid creates a monoid for types that are both Applicative and Alternative.
This combines the behavior of ApplicativeMonoid with Alternative semantics, providing both applicative combination and fallback/choice behavior. The resulting monoid tries to combine values using the applicative monoid, but falls back to alternative behavior when needed.
This is useful for types like Option, Either, or Parser that support both applicative composition and alternative/fallback semantics.
Type Parameters:
- A: The base type with a monoid
- HKTA: The higher-kinded type representing the functor applied to A
- HKTFA: The higher-kinded type representing the functor applied to func(A) A
- LAZYHKTA: A lazy/deferred computation of HKTA (typically func() HKTA)
Parameters:
- fof: The "pure" operation that lifts a value into the context
- fmap: The map operation for the functor
- fap: The apply operation for the applicative
- falt: The alternative operation providing fallback/choice behavior
- m: The monoid for the base type A
Returns:
- A Monoid[HKTA] combining applicative and alternative semantics
Example (conceptual with Option-like type):
intAddMonoid := MakeMonoid(
func(a, b int) int { return a + b },
0,
)
optMonoid := AlternativeMonoid(
some, // pure
fmap, // map
fap, // apply
falt, // alternative (fallback)
intAddMonoid,
)
// Combines Some values using addition
result1 := optMonoid.Concat(Some(5), Some(3)) // Some(8)
// Falls back when first is None
result2 := optMonoid.Concat(None(), Some(3)) // Some(3)
func ApplicativeMonoid ¶
func ApplicativeMonoid[A, HKTA, HKTFA any]( fof func(A) HKTA, fmap func(HKTA, func(A) func(A) A) HKTFA, fap func(HKTFA, HKTA) HKTA, m Monoid[A], ) Monoid[HKTA]
ApplicativeMonoid lifts a monoid into an applicative functor context.
This function creates a monoid for applicative functor values (HKTA) given a monoid for the base type (A). It uses the applicative functor's operations (of, map, ap) to lift the monoid operations into the applicative context.
This is useful for combining values that are wrapped in applicative contexts (like Option, Either, IO, etc.) using the underlying monoid's combination logic.
Type Parameters:
- A: The base type with a monoid
- HKTA: The higher-kinded type representing the applicative functor applied to A
- HKTFA: The higher-kinded type representing the applicative functor applied to func(A) A
Parameters:
- fof: The "pure" or "of" operation that lifts a value into the applicative context
- fmap: The map operation for the applicative functor
- fap: The apply operation for the applicative functor
- m: The monoid for the base type A
Returns:
- A Monoid[HKTA] that combines applicative values using the base monoid
Example (conceptual with Option-like type):
type Option[A any] struct { value *A }
intAddMonoid := MakeMonoid(
func(a, b int) int { return a + b },
0,
)
optMonoid := ApplicativeMonoid(
some, // func(int) Option[int]
fmap, // func(Option[int], func(int) func(int) int) Option[func(int) int]
fap, // func(Option[func(int) int], Option[int]) Option[int]
intAddMonoid,
)
// Combine Option values using addition
result := optMonoid.Concat(Some(5), Some(3)) // Some(8)
empty := optMonoid.Empty() // Some(0)
func FunctionMonoid ¶
FunctionMonoid creates a monoid for functions when the codomain (return type) has a monoid.
Given a monoid for type B, this creates a monoid for functions of type func(A) B. The resulting monoid combines functions by combining their results using the provided monoid's Concat operation. The empty function always returns the monoid's Empty value.
This allows you to compose functions that return monoidal values in a point-wise manner.
Type Parameters:
- A: The domain (input type) of the functions
- B: The codomain (return type) of the functions, which must have a monoid
Parameters:
- m: A monoid for the codomain type B
Returns:
- A Monoid[func(A) B] that combines functions point-wise
Example:
// Monoid for functions returning integers
intAddMonoid := MakeMonoid(
func(a, b int) int { return a + b },
0,
)
funcMonoid := FunctionMonoid[string, int](intAddMonoid)
// Define some functions
f1 := func(s string) int { return len(s) }
f2 := func(s string) int { return len(s) * 2 }
// Combine functions: result(x) = f1(x) + f2(x)
combined := funcMonoid.Concat(f1, f2)
result := combined("hello") // len("hello") + len("hello")*2 = 5 + 10 = 15
// Empty function always returns 0
emptyFunc := funcMonoid.Empty()
result := emptyFunc("anything") // 0
// Verify identity laws
assert.Equal(t, f1("test"), funcMonoid.Concat(funcMonoid.Empty(), f1)("test"))
assert.Equal(t, f1("test"), funcMonoid.Concat(f1, funcMonoid.Empty())("test"))
func MakeMonoid ¶
MakeMonoid creates a monoid from a binary operation and an identity element.
The provided concat function must be associative, and the empty element must satisfy the identity laws (left and right identity).
Parameters:
- c: An associative binary operation func(A, A) A
- e: The identity element of type A
Returns:
- A Monoid[A] instance
Example:
// Integer addition monoid
addMonoid := MakeMonoid(
func(a, b int) int { return a + b },
0, // identity element
)
result := addMonoid.Concat(5, 3) // 8
empty := addMonoid.Empty() // 0
// String concatenation monoid
stringMonoid := MakeMonoid(
func(a, b string) string { return a + b },
"", // identity element
)
result := stringMonoid.Concat("Hello", " World") // "Hello World"
func Reverse ¶
Reverse returns the dual of a Monoid by swapping the arguments of Concat.
The reversed monoid has the same identity element but applies the binary operation in the opposite order. This is useful for operations that are not commutative.
Parameters:
- m: The monoid to reverse
Returns:
- A new Monoid[A] with reversed operation order
Example:
// Subtraction monoid (not commutative)
subMonoid := MakeMonoid(
func(a, b int) int { return a - b },
0,
)
reversedMonoid := Reverse(subMonoid)
result1 := subMonoid.Concat(10, 3) // 10 - 3 = 7
result2 := reversedMonoid.Concat(10, 3) // 3 - 10 = -7
// String concatenation
stringMonoid := MakeMonoid(
func(a, b string) string { return a + b },
"",
)
reversed := Reverse(stringMonoid)
result := reversed.Concat("Hello", "World") // "WorldHello"