Skip to main content
Version: v2.2.82 (latest)
Reference · Collections

Record Monoid

Combining maps using monoid operations. Records form monoids under various operations, enabling powerful composition patterns.


Core API

FunctionSignatureDescription
Foldfunc Fold[K comparable, V any](Monoid[V]) func(map[K]V) VFold map to value
FoldMapfunc FoldMap[K comparable, A, B any](Monoid[B]) func(func(A) B) func(map[K]A) BMap then fold
Unionfunc Union[K comparable, V any](Magma[V]) func(map[K]V) func(map[K]V) map[K]VMerge with function

Usage Examples

Fold - Basic

fold.go
import (
  R "github.com/IBM/fp-go/v2/record"
  M "github.com/IBM/fp-go/v2/monoid"
  F "github.com/IBM/fp-go/v2/function"
)

m := map[string]int{"a": 1, "b": 2, "c": 3}

// Sum all values
sum := F.Pipe2(
  m,
  R.Fold(M.MonoidSum[int]()),
)
// 6

// Product of all values
product := F.Pipe2(
  m,
  R.Fold(M.MonoidProduct[int]()),
)
// 6

FoldMap - Transform and Fold

foldmap.go
type Product struct {
  Name  string
  Price float64
}

products := map[string]Product{
  "laptop": {Name: "Laptop", Price: 999},
  "mouse":  {Name: "Mouse", Price: 29},
  "keyboard": {Name: "Keyboard", Price: 79},
}

// Calculate total price
total := F.Pipe2(
  products,
  R.FoldMap(M.MonoidSum[float64]())(func(p Product) float64 {
      return p.Price
  }),
)
// 1107.0

String Concatenation

concat.go
m := map[string]string{
  "first":  "Hello",
  "second": "World",
  "third":  "!",
}

// Concatenate all values
result := F.Pipe2(
  m,
  R.Fold(M.MonoidString),
)
// "HelloWorld!" (order may vary)

// With separator
import S "github.com/IBM/fp-go/v2/string"

separated := F.Pipe2(
  m,
  R.FoldMap(M.MonoidString)(func(s string) string {
      return s + " "
  }),
)
// "Hello World ! " (order may vary)

Union - Merge Maps

union.go
m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"b": 3, "c": 4}

// Merge with sum
merged := R.Union(M.MonoidSum[int]())(m2)(m1)
// map[string]int{"a": 1, "b": 5, "c": 4}

// Merge with max
import Mg "github.com/IBM/fp-go/v2/magma"

maxMagma := Mg.MakeMagma(func(x, y int) int {
  if x > y {
      return x
  }
  return y
})

maxMerged := R.Union(maxMagma)(m2)(m1)
// map[string]int{"a": 1, "b": 3, "c": 4}

Collecting Arrays

arrays.go
import A "github.com/IBM/fp-go/v2/array"

m := map[string][]int{
  "group1": {1, 2, 3},
  "group2": {4, 5},
  "group3": {6},
}

// Concatenate all arrays
allValues := F.Pipe2(
  m,
  R.Fold(M.MonoidArray[int]()),
)
// []int{1, 2, 3, 4, 5, 6} (order may vary)

Combining Configurations

config.go
type Config struct {
  MaxRetries int
  Timeout    int
}

configs := map[string]Config{
  "service1": {MaxRetries: 3, Timeout: 30},
  "service2": {MaxRetries: 5, Timeout: 60},
}

// Find max values
maxConfig := F.Pipe2(
  configs,
  R.FoldMap(
      M.MakeMonoid(
          func(a, b Config) Config {
              return Config{
                  MaxRetries: max(a.MaxRetries, b.MaxRetries),
                  Timeout:    max(a.Timeout, b.Timeout),
              }
          },
          Config{MaxRetries: 0, Timeout: 0},
      ),
  )(F.Identity[Config]),
)
// Config{MaxRetries: 5, Timeout: 60}

Aggregating Statistics

stats.go
type Stats struct {
  Count int
  Sum   float64
}

data := map[string][]float64{
  "group1": {1.0, 2.0, 3.0},
  "group2": {4.0, 5.0},
}

// Calculate combined statistics
statsMonoid := M.MakeMonoid(
  func(a, b Stats) Stats {
      return Stats{
          Count: a.Count + b.Count,
          Sum:   a.Sum + b.Sum,
      }
  },
  Stats{Count: 0, Sum: 0},
)

combined := F.Pipe2(
  data,
  R.FoldMap(statsMonoid)(func(values []float64) Stats {
      sum := 0.0
      for _, v := range values {
          sum += v
      }
      return Stats{Count: len(values), Sum: sum}
  }),
)
// Stats{Count: 5, Sum: 15.0}

Common Patterns

Merging Multiple Maps

merge_multiple.go
maps := []map[string]int{
  {"a": 1, "b": 2},
  {"b": 3, "c": 4},
  {"c": 5, "d": 6},
}

// Merge all with sum
import A "github.com/IBM/fp-go/v2/array"

result := F.Pipe2(
  maps,
  A.Reduce(
      func(acc, m map[string]int) map[string]int {
          return R.Union(M.MonoidSum[int]())(m)(acc)
      },
      map[string]int{},
  ),
)
// map[string]int{"a": 1, "b": 5, "c": 9, "d": 6}

Accumulating Results

accumulate.go
type Result struct {
  Success int
  Failed  int
}

results := map[string]Result{
  "batch1": {Success: 10, Failed: 2},
  "batch2": {Success: 15, Failed: 1},
  "batch3": {Success: 8, Failed: 3},
}

resultMonoid := M.MakeMonoid(
  func(a, b Result) Result {
      return Result{
          Success: a.Success + b.Success,
          Failed:  a.Failed + b.Failed,
      }
  },
  Result{Success: 0, Failed: 0},
)

total := F.Pipe2(
  results,
  R.Fold(resultMonoid),
)
// Result{Success: 33, Failed: 6}

Building Indexes

index.go
type Index map[string][]string

indexes := map[string]Index{
  "doc1": {"word1": {"doc1"}, "word2": {"doc1"}},
  "doc2": {"word1": {"doc2"}, "word3": {"doc2"}},
}

// Merge indexes
appendMagma := Mg.MakeMagma(func(x, y []string) []string {
  return append(x, y...)
})

combined := F.Pipe2(
  indexes,
  R.Fold(R.Union(appendMagma)),
)
// Index{
//   "word1": ["doc1", "doc2"],
//   "word2": ["doc1"],
//   "word3": ["doc2"],
// }

Monoid Laws: Monoids must satisfy associativity and have an identity element. This ensures that folding operations produce consistent results regardless of evaluation order.

Order Independence: Since Go maps have undefined iteration order, fold operations should use commutative monoids for predictable results.