monoid

package
v2.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 18, 2025 License: Apache-2.0 Imports: 2 Imported by: 0

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:

  1. Associativity (from Semigroup): Concat(Concat(x, y), z) = Concat(x, Concat(y, z))

  2. Left Identity: Concat(Empty(), x) = x

  3. 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
  • semigroup: Parent structure (associative binary operation)
  • magma: Grandparent structure (binary operation)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConcatAll

func ConcatAll[A any](m Monoid[A]) func([]A) A

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

func Fold[A any](m Monoid[A]) func([]A) A

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

func GenericConcatAll[GA ~[]A, A any](m Monoid[A]) func(GA) A

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

func ToSemigroup[A any](m Monoid[A]) S.Semigroup[A]

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

type Monoid[A any] interface {
	S.Semigroup[A]
	Empty() A
}

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

func FunctionMonoid[A, B any](m Monoid[B]) Monoid[func(A) B]

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

func MakeMonoid[A any](c func(A, A) A, e A) Monoid[A]

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

func Reverse[A any](m Monoid[A]) Monoid[A]

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"

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL