mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
104 lines
3.3 KiB
Go
104 lines
3.3 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 eq
|
|
|
|
// Contramap creates an Eq[B] from an Eq[A] by providing a function that maps B to A.
|
|
// This is a contravariant functor operation that allows you to transform equality predicates
|
|
// by mapping the input type. It's particularly useful for comparing complex types by
|
|
// extracting comparable fields.
|
|
//
|
|
// The name "contramap" comes from category theory, where it represents a contravariant
|
|
// functor. Unlike regular map (covariant), which transforms the output, contramap
|
|
// transforms the input in the opposite direction.
|
|
//
|
|
// Type Parameters:
|
|
// - A: The type that has an existing Eq instance
|
|
// - B: The type for which we want to create an Eq instance
|
|
//
|
|
// Parameters:
|
|
// - f: A function that extracts or converts a value of type B to type A
|
|
//
|
|
// Returns:
|
|
// - A function that takes an Eq[A] and returns an Eq[B]
|
|
//
|
|
// The resulting Eq[B] compares two B values by:
|
|
// 1. Applying f to both values to get A values
|
|
// 2. Using the original Eq[A] to compare those A values
|
|
//
|
|
// Example - Compare structs by a single field:
|
|
//
|
|
// 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, different names
|
|
//
|
|
// Example - Case-insensitive string comparison:
|
|
//
|
|
// type User struct {
|
|
// Username string
|
|
// Email string
|
|
// }
|
|
//
|
|
// caseInsensitiveEq := eq.FromEquals(func(a, b string) bool {
|
|
// return strings.EqualFold(a, b)
|
|
// })
|
|
//
|
|
// userEqByUsername := eq.Contramap(func(u User) string {
|
|
// return u.Username
|
|
// })(caseInsensitiveEq)
|
|
//
|
|
// u1 := User{Username: "Alice", Email: "alice@example.com"}
|
|
// u2 := User{Username: "ALICE", Email: "different@example.com"}
|
|
// assert.True(t, userEqByUsername.Equals(u1, u2)) // Case-insensitive match
|
|
//
|
|
// Example - Nested field access:
|
|
//
|
|
// type Address struct {
|
|
// City string
|
|
// }
|
|
//
|
|
// type Person struct {
|
|
// Name string
|
|
// Address Address
|
|
// }
|
|
//
|
|
// // Compare persons by city
|
|
// personEqByCity := eq.Contramap(func(p Person) string {
|
|
// return p.Address.City
|
|
// })(eq.FromStrictEquals[string]())
|
|
//
|
|
// Contramap Law:
|
|
// Contramap must satisfy: Contramap(f)(Contramap(g)(eq)) = Contramap(g ∘ f)(eq)
|
|
// This means contramapping twice is the same as contramapping with the composed function.
|
|
func Contramap[A, B any](f func(b B) A) func(Eq[A]) Eq[B] {
|
|
return func(fa Eq[A]) Eq[B] {
|
|
equals := fa.Equals
|
|
return FromEquals(func(x, y B) bool {
|
|
return equals(f(x), f(y))
|
|
})
|
|
}
|
|
}
|