1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-25 22:21:49 +02:00
Files
fp-go/v2/eq/doc.go

207 lines
6.0 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 eq provides type-safe equality comparisons for any type in Go.
# Overview
The eq package implements the Eq type class from functional programming, which
represents types that support equality comparison. Unlike Go's built-in == operator
which only works with comparable types, Eq allows defining custom equality semantics
for any type, including complex structures, functions, and non-comparable types.
# Core Concepts
The Eq[T] interface represents an equality predicate for type T:
type Eq[T any] interface {
Equals(x, y T) bool
}
This abstraction enables:
- Custom equality semantics for any type
- Composition of equality predicates
- Contravariant mapping to transform equality predicates
- Monoid structure for combining multiple equality checks
# Basic Usage
Creating equality predicates for comparable types:
// For built-in comparable types
intEq := eq.FromStrictEquals[int]()
assert.True(t, intEq.Equals(42, 42))
assert.False(t, intEq.Equals(42, 43))
stringEq := eq.FromStrictEquals[string]()
assert.True(t, stringEq.Equals("hello", "hello"))
Creating custom equality predicates:
// Case-insensitive string equality
caseInsensitiveEq := eq.FromEquals(func(a, b string) bool {
return strings.EqualFold(a, b)
})
assert.True(t, caseInsensitiveEq.Equals("Hello", "HELLO"))
// Approximate float equality
approxEq := eq.FromEquals(func(a, b float64) bool {
return math.Abs(a-b) < 0.0001
})
assert.True(t, approxEq.Equals(1.0, 1.00009))
# Contramap - Transforming Equality
Contramap allows you to create an equality predicate for type B from an equality
predicate for type A, given a function from B to A. This is useful for comparing
complex types by extracting comparable fields:
type Person struct {
ID int
Name string
Age int
}
// Compare persons by ID only
personEqByID := eq.Contramap(func(p Person) int {
return p.ID
})(eq.FromStrictEquals[int]())
p1 := Person{ID: 1, Name: "Alice", Age: 30}
p2 := Person{ID: 1, Name: "Bob", Age: 25}
assert.True(t, personEqByID.Equals(p1, p2)) // Same ID
// Compare persons by name
personEqByName := eq.Contramap(func(p Person) string {
return p.Name
})(eq.FromStrictEquals[string]())
assert.False(t, personEqByName.Equals(p1, p2)) // Different names
# Semigroup and Monoid
The eq package provides Semigroup and Monoid instances for Eq[A], allowing you to
combine multiple equality predicates using logical AND:
type User struct {
Username string
Email string
}
// Compare by username
usernameEq := eq.Contramap(func(u User) string {
return u.Username
})(eq.FromStrictEquals[string]())
// Compare by email
emailEq := eq.Contramap(func(u User) string {
return u.Email
})(eq.FromStrictEquals[string]())
// Combine: users are equal if BOTH username AND email match
userEq := eq.Semigroup[User]().Concat(usernameEq, emailEq)
u1 := User{Username: "alice", Email: "alice@example.com"}
u2 := User{Username: "alice", Email: "alice@example.com"}
u3 := User{Username: "alice", Email: "different@example.com"}
assert.True(t, userEq.Equals(u1, u2)) // Both match
assert.False(t, userEq.Equals(u1, u3)) // Email differs
The Monoid provides an identity element (Empty) that always returns true:
monoid := eq.Monoid[int]()
alwaysTrue := monoid.Empty()
assert.True(t, alwaysTrue.Equals(1, 2)) // Always true
# Curried Equality
The Equals function provides a curried version of equality checking, useful for
partial application and functional composition:
intEq := eq.FromStrictEquals[int]()
equals42 := eq.Equals(intEq)(42)
assert.True(t, equals42(42))
assert.False(t, equals42(43))
// Use in higher-order functions
numbers := []int{40, 41, 42, 43, 44}
filtered := array.Filter(equals42)(numbers)
// filtered = [42]
# Advanced Examples
Comparing slices element-wise:
sliceEq := eq.FromEquals(func(a, b []int) bool {
if len(a) != len(b) {
return false
}
intEq := eq.FromStrictEquals[int]()
for i := range a {
if !intEq.Equals(a[i], b[i]) {
return false
}
}
return true
})
assert.True(t, sliceEq.Equals([]int{1, 2, 3}, []int{1, 2, 3}))
assert.False(t, sliceEq.Equals([]int{1, 2, 3}, []int{1, 2, 4}))
Comparing maps:
mapEq := eq.FromEquals(func(a, b map[string]int) bool {
if len(a) != len(b) {
return false
}
for k, v := range a {
if bv, ok := b[k]; !ok || v != bv {
return false
}
}
return true
})
# Type Class Laws
Eq instances should satisfy the following laws:
1. Reflexivity: For all x, Equals(x, x) = true
2. Symmetry: For all x, y, Equals(x, y) = Equals(y, x)
3. Transitivity: If Equals(x, y) and Equals(y, z), then Equals(x, z)
These laws ensure that Eq behaves as a proper equivalence relation.
# Functions
- FromStrictEquals[T comparable]() - Create Eq from Go's == operator
- FromEquals[T any](func(x, y T) bool) - Create Eq from custom comparison
- Empty[T any]() - Create Eq that always returns true
- Equals[T any](Eq[T]) - Curried equality checking
- Contramap[A, B any](func(B) A) - Transform Eq by mapping input type
- Semigroup[A any]() - Combine Eq instances with logical AND
- Monoid[A any]() - Semigroup with identity element
# Related Packages
- ord: Provides ordering comparisons (less than, greater than)
- semigroup: Provides the Semigroup abstraction used by Eq
- monoid: Provides the Monoid abstraction used by Eq
*/
package eq