mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
* 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>
247 lines
6.2 KiB
Go
247 lines
6.2 KiB
Go
// 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 magma provides the Magma algebraic structure.
|
|
|
|
# Overview
|
|
|
|
A Magma is the most basic algebraic structure in abstract algebra. It consists
|
|
of a set equipped with a single binary operation (Concat) that combines two
|
|
elements to produce another element of the same type. Unlike more constrained
|
|
structures like Semigroup or Monoid, a Magma's operation doesn't need to be
|
|
associative or have an identity element.
|
|
|
|
The Magma interface:
|
|
|
|
type Magma[A any] interface {
|
|
Concat(x A, y A) A
|
|
}
|
|
|
|
This simple structure serves as the foundation for more complex algebraic
|
|
structures in the type class hierarchy:
|
|
- Magma (no laws)
|
|
- Semigroup (adds associativity)
|
|
- Monoid (adds identity element)
|
|
|
|
# Basic Usage
|
|
|
|
Creating and using magmas:
|
|
|
|
// Create a magma for integer addition
|
|
addMagma := magma.MakeMagma(func(a, b int) int {
|
|
return a + b
|
|
})
|
|
|
|
result := addMagma.Concat(5, 3)
|
|
// result is 8
|
|
|
|
// Create a magma for string concatenation
|
|
stringMagma := magma.MakeMagma(func(a, b string) string {
|
|
return a + b
|
|
})
|
|
|
|
result := stringMagma.Concat("Hello", " World")
|
|
// result is "Hello World"
|
|
|
|
# Built-in Magmas
|
|
|
|
The package provides several pre-defined magmas:
|
|
|
|
First - Always returns the first argument:
|
|
|
|
m := magma.First[int]()
|
|
result := m.Concat(1, 2)
|
|
// result is 1
|
|
|
|
Second - Always returns the second argument:
|
|
|
|
m := magma.Second[int]()
|
|
result := m.Concat(1, 2)
|
|
// result is 2
|
|
|
|
# Transforming Magmas
|
|
|
|
Reverse - Swaps the order of arguments:
|
|
|
|
addMagma := magma.MakeMagma(func(a, b int) int {
|
|
return a - b
|
|
})
|
|
|
|
reversedMagma := magma.Reverse(addMagma)
|
|
|
|
result1 := addMagma.Concat(10, 3) // 10 - 3 = 7
|
|
result2 := reversedMagma.Concat(10, 3) // 3 - 10 = -7
|
|
|
|
FilterFirst - Only applies operation if first argument satisfies predicate:
|
|
|
|
addMagma := magma.MakeMagma(func(a, b int) int {
|
|
return a + b
|
|
})
|
|
|
|
// Only add if first number is positive
|
|
filteredMagma := magma.FilterFirst(func(n int) bool {
|
|
return n > 0
|
|
})(addMagma)
|
|
|
|
result1 := filteredMagma.Concat(5, 3) // 5 + 3 = 8 (5 is positive)
|
|
result2 := filteredMagma.Concat(-5, 3) // 3 (5 is negative, return second)
|
|
|
|
FilterSecond - Only applies operation if second argument satisfies predicate:
|
|
|
|
addMagma := magma.MakeMagma(func(a, b int) int {
|
|
return a + b
|
|
})
|
|
|
|
// Only add if second number is positive
|
|
filteredMagma := magma.FilterSecond(func(n int) bool {
|
|
return n > 0
|
|
})(addMagma)
|
|
|
|
result1 := filteredMagma.Concat(5, 3) // 5 + 3 = 8 (3 is positive)
|
|
result2 := filteredMagma.Concat(5, -3) // 5 (-3 is negative, return first)
|
|
|
|
Endo - Applies a function to both arguments before combining:
|
|
|
|
addMagma := magma.MakeMagma(func(a, b int) int {
|
|
return a + b
|
|
})
|
|
|
|
// Double both numbers before adding
|
|
doubledMagma := magma.Endo(func(n int) int {
|
|
return n * 2
|
|
})(addMagma)
|
|
|
|
result := doubledMagma.Concat(3, 4)
|
|
// (3*2) + (4*2) = 6 + 8 = 14
|
|
|
|
# Array Operations
|
|
|
|
ConcatAll - Combines all elements in a slice using a magma:
|
|
|
|
addMagma := magma.MakeMagma(func(a, b int) int {
|
|
return a + b
|
|
})
|
|
|
|
numbers := []int{1, 2, 3, 4, 5}
|
|
result := magma.ConcatAll(addMagma)(0)(numbers)
|
|
// 0 + 1 + 2 + 3 + 4 + 5 = 15
|
|
|
|
MonadConcatAll - Uncurried version:
|
|
|
|
addMagma := magma.MakeMagma(func(a, b int) int {
|
|
return a + b
|
|
})
|
|
|
|
numbers := []int{1, 2, 3, 4, 5}
|
|
result := magma.MonadConcatAll(addMagma)(numbers, 0)
|
|
// 0 + 1 + 2 + 3 + 4 + 5 = 15
|
|
|
|
# Generic Array Operations
|
|
|
|
The package provides generic versions that work with custom slice types:
|
|
|
|
type IntSlice []int
|
|
|
|
addMagma := magma.MakeMagma(func(a, b int) int {
|
|
return a + b
|
|
})
|
|
|
|
numbers := IntSlice{1, 2, 3}
|
|
result := magma.GenericConcatAll[IntSlice](addMagma)(0)(numbers)
|
|
// result is 6
|
|
|
|
# Practical Examples
|
|
|
|
Building a max magma:
|
|
|
|
maxMagma := magma.MakeMagma(func(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
})
|
|
|
|
numbers := []int{3, 7, 2, 9, 1}
|
|
maximum := magma.ConcatAll(maxMagma)(0)(numbers)
|
|
// maximum is 9
|
|
|
|
Building a min magma:
|
|
|
|
minMagma := magma.MakeMagma(func(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
})
|
|
|
|
numbers := []int{3, 7, 2, 9, 1}
|
|
minimum := magma.ConcatAll(minMagma)(10)(numbers)
|
|
// minimum is 1
|
|
|
|
Combining strings with separator:
|
|
|
|
joinMagma := magma.MakeMagma(func(a, b string) string {
|
|
if a == "" {
|
|
return b
|
|
}
|
|
if b == "" {
|
|
return a
|
|
}
|
|
return a + ", " + b
|
|
})
|
|
|
|
words := []string{"apple", "banana", "cherry"}
|
|
result := magma.ConcatAll(joinMagma)("")(words)
|
|
// result is "apple, banana, cherry"
|
|
|
|
# Relationship to Other Structures
|
|
|
|
Magma is the base of the algebraic hierarchy:
|
|
|
|
Magma (no laws)
|
|
↓
|
|
Semigroup (associative)
|
|
↓
|
|
Monoid (identity element)
|
|
|
|
Any Semigroup or Monoid is also a Magma, but not vice versa.
|
|
|
|
# Functions
|
|
|
|
Core operations:
|
|
- MakeMagma[A any](func(A, A) A) - Create a magma from a binary operation
|
|
- First[A any]() - Magma that returns first argument
|
|
- Second[A any]() - Magma that returns second argument
|
|
|
|
Transformations:
|
|
- Reverse[A any](Magma[A]) - Swap argument order
|
|
- FilterFirst[A any](func(A) bool) - Filter based on first argument
|
|
- FilterSecond[A any](func(A) bool) - Filter based on second argument
|
|
- Endo[A any](func(A) A) - Apply function before combining
|
|
|
|
Array operations:
|
|
- ConcatAll[A any](Magma[A]) - Combine all elements (curried)
|
|
- MonadConcatAll[A any](Magma[A]) - Combine all elements (uncurried)
|
|
- GenericConcatAll[GA ~[]A, A any](Magma[A]) - Generic version (curried)
|
|
- GenericMonadConcatAll[GA ~[]A, A any](Magma[A]) - Generic version (uncurried)
|
|
|
|
# Related Packages
|
|
|
|
- semigroup: Adds associativity law to Magma
|
|
- monoid: Adds identity element to Semigroup
|
|
*/
|
|
package magma
|