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 endomorphism
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"github.com/IBM/fp-go/v2/function"
|
|
|
|
|
)
|
|
|
|
|
|
2025-11-12 15:03:55 +01:00
|
|
|
// MonadAp applies an endomorphism in a function to an endomorphism value.
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
2025-11-12 15:03:55 +01:00
|
|
|
// For endomorphisms, Ap composes two endomorphisms using RIGHT-TO-LEFT composition.
|
|
|
|
|
// This is the applicative functor operation for endomorphisms.
|
|
|
|
|
//
|
|
|
|
|
// IMPORTANT: Execution order is RIGHT-TO-LEFT (same as MonadCompose):
|
|
|
|
|
// - fa is applied first to the input
|
|
|
|
|
// - fab is applied to the result
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Parameters:
|
2025-11-12 15:03:55 +01:00
|
|
|
// - fab: An endomorphism to apply (outer function)
|
|
|
|
|
// - fa: An endomorphism to apply first (inner function)
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Returns:
|
2025-11-12 15:03:55 +01:00
|
|
|
// - A new endomorphism that applies fa, then fab
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-12 15:03:55 +01:00
|
|
|
// increment := func(x int) int { return x + 1 }
|
|
|
|
|
// result := endomorphism.MonadAp(double, increment) // Composes: double ∘ increment
|
|
|
|
|
// // result(5) = double(increment(5)) = double(6) = 12
|
|
|
|
|
func MonadAp[A any](fab Endomorphism[A], fa Endomorphism[A]) Endomorphism[A] {
|
|
|
|
|
return MonadCompose(fab, fa)
|
2025-11-06 09:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-12 15:03:55 +01:00
|
|
|
// Ap returns a function that applies an endomorphism to another endomorphism.
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
2025-11-12 15:03:55 +01:00
|
|
|
// This is the curried version of MonadAp. It takes an endomorphism fa and returns
|
|
|
|
|
// a function that composes any endomorphism with fa using RIGHT-TO-LEFT composition.
|
|
|
|
|
//
|
|
|
|
|
// IMPORTANT: Execution order is RIGHT-TO-LEFT:
|
|
|
|
|
// - fa is applied first to the input
|
|
|
|
|
// - The endomorphism passed to the returned function is applied to the result
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Parameters:
|
2025-11-12 15:03:55 +01:00
|
|
|
// - fa: The first endomorphism to apply (inner function)
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Returns:
|
2025-11-12 15:03:55 +01:00
|
|
|
// - A function that takes an endomorphism and composes it with fa (right-to-left)
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
2025-11-12 15:03:55 +01:00
|
|
|
// increment := func(x int) int { return x + 1 }
|
|
|
|
|
// applyIncrement := endomorphism.Ap(increment)
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-12 15:03:55 +01:00
|
|
|
// composed := applyIncrement(double) // double ∘ increment
|
|
|
|
|
// // composed(5) = double(increment(5)) = double(6) = 12
|
|
|
|
|
func Ap[A any](fa Endomorphism[A]) Operator[A] {
|
|
|
|
|
return Compose(fa)
|
2025-11-06 09:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-12 13:51:00 +01:00
|
|
|
// MonadCompose composes two endomorphisms, executing them from right to left.
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
2025-11-12 13:51:00 +01:00
|
|
|
// MonadCompose creates a new endomorphism that applies f2 first, then f1.
|
|
|
|
|
// This follows the mathematical notation of function composition: (f1 ∘ f2)(x) = f1(f2(x))
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
2025-11-12 13:51:00 +01:00
|
|
|
// IMPORTANT: The execution order is RIGHT-TO-LEFT:
|
|
|
|
|
// - f2 is applied first to the input
|
|
|
|
|
// - f1 is applied to the result of f2
|
|
|
|
|
//
|
|
|
|
|
// This is different from Chain/MonadChain which executes LEFT-TO-RIGHT.
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Parameters:
|
2025-11-12 13:51:00 +01:00
|
|
|
// - f1: The second function to apply (outer function)
|
|
|
|
|
// - f2: The first function to apply (inner function)
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Returns:
|
2025-11-12 13:51:00 +01:00
|
|
|
// - A new endomorphism that applies f2, then f1
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-06 09:27:00 +01:00
|
|
|
// increment := func(x int) int { return x + 1 }
|
2025-11-12 13:51:00 +01:00
|
|
|
//
|
|
|
|
|
// // MonadCompose executes RIGHT-TO-LEFT: increment first, then double
|
|
|
|
|
// composed := endomorphism.MonadCompose(double, increment)
|
|
|
|
|
// result := composed(5) // (5 + 1) * 2 = 12
|
|
|
|
|
//
|
|
|
|
|
// // Compare with Chain which executes LEFT-TO-RIGHT:
|
|
|
|
|
// chained := endomorphism.MonadChain(double, increment)
|
|
|
|
|
// result2 := chained(5) // (5 * 2) + 1 = 11
|
|
|
|
|
func MonadCompose[A any](f, g Endomorphism[A]) Endomorphism[A] {
|
|
|
|
|
return function.Flow2(g, f)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-12 15:03:55 +01:00
|
|
|
// MonadMap maps an endomorphism over another endomorphism using function composition.
|
|
|
|
|
//
|
|
|
|
|
// For endomorphisms, Map is equivalent to Compose (RIGHT-TO-LEFT composition).
|
|
|
|
|
// This is the functor map operation for endomorphisms.
|
|
|
|
|
//
|
|
|
|
|
// IMPORTANT: Execution order is RIGHT-TO-LEFT:
|
|
|
|
|
// - g is applied first to the input
|
|
|
|
|
// - f is applied to the result
|
|
|
|
|
//
|
|
|
|
|
// Parameters:
|
|
|
|
|
// - f: The function to map (outer function)
|
|
|
|
|
// - g: The endomorphism to map over (inner function)
|
|
|
|
|
//
|
|
|
|
|
// Returns:
|
|
|
|
|
// - A new endomorphism that applies g, then f
|
|
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-12 15:03:55 +01:00
|
|
|
// increment := func(x int) int { return x + 1 }
|
|
|
|
|
// mapped := endomorphism.MonadMap(double, increment)
|
|
|
|
|
// // mapped(5) = double(increment(5)) = double(6) = 12
|
|
|
|
|
func MonadMap[A any](f, g Endomorphism[A]) Endomorphism[A] {
|
|
|
|
|
return MonadCompose(f, g)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-12 13:51:00 +01:00
|
|
|
// Compose returns a function that composes an endomorphism with another, executing right to left.
|
|
|
|
|
//
|
|
|
|
|
// This is the curried version of MonadCompose. It takes an endomorphism g and returns
|
|
|
|
|
// a function that composes any endomorphism with g, applying g first (inner function),
|
|
|
|
|
// then the input endomorphism (outer function).
|
|
|
|
|
//
|
|
|
|
|
// IMPORTANT: Execution order is RIGHT-TO-LEFT (mathematical composition):
|
|
|
|
|
// - g is applied first to the input
|
|
|
|
|
// - The endomorphism passed to the returned function is applied to the result of g
|
|
|
|
|
//
|
|
|
|
|
// This follows the mathematical composition notation where Compose(g)(f) = f ∘ g
|
|
|
|
|
//
|
|
|
|
|
// Parameters:
|
|
|
|
|
// - g: The first endomorphism to apply (inner function)
|
|
|
|
|
//
|
|
|
|
|
// Returns:
|
|
|
|
|
// - A function that takes an endomorphism f and composes it with g (right-to-left)
|
|
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
|
|
|
|
// increment := func(x int) int { return x + 1 }
|
|
|
|
|
// composeWithIncrement := endomorphism.Compose(increment)
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-12 13:51:00 +01:00
|
|
|
//
|
|
|
|
|
// // Composes double with increment (RIGHT-TO-LEFT: increment first, then double)
|
|
|
|
|
// composed := composeWithIncrement(double)
|
|
|
|
|
// result := composed(5) // (5 + 1) * 2 = 12
|
|
|
|
|
//
|
|
|
|
|
// // Compare with Chain which executes LEFT-TO-RIGHT:
|
|
|
|
|
// chainWithIncrement := endomorphism.Chain(increment)
|
|
|
|
|
// chained := chainWithIncrement(double)
|
|
|
|
|
// result2 := chained(5) // (5 * 2) + 1 = 11
|
2025-11-12 15:03:55 +01:00
|
|
|
func Compose[A any](g Endomorphism[A]) Operator[A] {
|
2025-11-12 13:51:00 +01:00
|
|
|
return function.Bind2nd(MonadCompose, g)
|
2025-11-06 09:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-12 15:03:55 +01:00
|
|
|
// Map returns a function that maps an endomorphism over another endomorphism.
|
|
|
|
|
//
|
|
|
|
|
// This is the curried version of MonadMap. It takes an endomorphism f and returns
|
|
|
|
|
// a function that maps f over any endomorphism using RIGHT-TO-LEFT composition.
|
|
|
|
|
//
|
|
|
|
|
// IMPORTANT: Execution order is RIGHT-TO-LEFT (same as Compose):
|
|
|
|
|
// - The endomorphism passed to the returned function is applied first
|
|
|
|
|
// - f is applied to the result
|
|
|
|
|
//
|
|
|
|
|
// For endomorphisms, Map is equivalent to Compose.
|
|
|
|
|
//
|
|
|
|
|
// Parameters:
|
|
|
|
|
// - f: The function to map (outer function)
|
|
|
|
|
//
|
|
|
|
|
// Returns:
|
|
|
|
|
// - A function that takes an endomorphism and maps f over it (right-to-left)
|
|
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-12 15:03:55 +01:00
|
|
|
// mapDouble := endomorphism.Map(double)
|
|
|
|
|
// increment := func(x int) int { return x + 1 }
|
|
|
|
|
// mapped := mapDouble(increment)
|
|
|
|
|
// // mapped(5) = double(increment(5)) = double(6) = 12
|
|
|
|
|
func Map[A any](f Endomorphism[A]) Operator[A] {
|
|
|
|
|
return Compose(f)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-12 13:51:00 +01:00
|
|
|
// MonadChain chains two endomorphisms together, executing them from left to right.
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
2025-11-12 15:03:55 +01:00
|
|
|
// This is the monadic bind operation for endomorphisms. For endomorphisms, bind is
|
|
|
|
|
// simply left-to-right function composition: ma is applied first, then f.
|
2025-11-12 13:51:00 +01:00
|
|
|
//
|
|
|
|
|
// IMPORTANT: The execution order is LEFT-TO-RIGHT:
|
2025-11-12 15:03:55 +01:00
|
|
|
// - ma is applied first to the input
|
|
|
|
|
// - f is applied to the result of ma
|
2025-11-12 13:51:00 +01:00
|
|
|
//
|
2025-11-12 15:03:55 +01:00
|
|
|
// This is different from MonadCompose which executes RIGHT-TO-LEFT.
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Parameters:
|
2025-11-12 15:03:55 +01:00
|
|
|
// - ma: The first endomorphism to apply
|
|
|
|
|
// - f: The second endomorphism to apply
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Returns:
|
2025-11-12 13:51:00 +01:00
|
|
|
// - A new endomorphism that applies ma, then f
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-06 09:27:00 +01:00
|
|
|
// increment := func(x int) int { return x + 1 }
|
2025-11-12 13:51:00 +01:00
|
|
|
//
|
|
|
|
|
// // MonadChain executes LEFT-TO-RIGHT: double first, then increment
|
2025-11-06 09:27:00 +01:00
|
|
|
// chained := endomorphism.MonadChain(double, increment)
|
|
|
|
|
// result := chained(5) // (5 * 2) + 1 = 11
|
2025-11-12 13:51:00 +01:00
|
|
|
//
|
2025-11-12 15:03:55 +01:00
|
|
|
// // Compare with MonadCompose which executes RIGHT-TO-LEFT:
|
|
|
|
|
// composed := endomorphism.MonadCompose(increment, double)
|
2025-11-12 13:51:00 +01:00
|
|
|
// result2 := composed(5) // (5 * 2) + 1 = 11 (same result, different parameter order)
|
2025-11-12 15:03:55 +01:00
|
|
|
func MonadChain[A any](ma Endomorphism[A], f Endomorphism[A]) Endomorphism[A] {
|
|
|
|
|
return function.Flow2(ma, f)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MonadChainFirst chains two endomorphisms but returns the result of the first.
|
|
|
|
|
//
|
|
|
|
|
// This applies ma first, then f, but discards the result of f and returns the result of ma.
|
|
|
|
|
// Useful for performing side-effects while preserving the original value.
|
|
|
|
|
//
|
|
|
|
|
// Parameters:
|
|
|
|
|
// - ma: The endomorphism whose result to keep
|
|
|
|
|
// - f: The endomorphism to apply for its effect
|
|
|
|
|
//
|
|
|
|
|
// Returns:
|
|
|
|
|
// - A new endomorphism that applies both but returns ma's result
|
|
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-12 15:03:55 +01:00
|
|
|
// log := func(x int) int { fmt.Println(x); return x }
|
|
|
|
|
// chained := endomorphism.MonadChainFirst(double, log)
|
|
|
|
|
// result := chained(5) // Prints 10, returns 10
|
|
|
|
|
func MonadChainFirst[A any](ma Endomorphism[A], f Endomorphism[A]) Endomorphism[A] {
|
|
|
|
|
return func(a A) A {
|
|
|
|
|
result := ma(a)
|
|
|
|
|
f(result) // Apply f for its effect
|
|
|
|
|
return result // But return ma's result
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ChainFirst returns a function that chains for effect but preserves the original result.
|
|
|
|
|
//
|
|
|
|
|
// This is the curried version of MonadChainFirst.
|
|
|
|
|
//
|
|
|
|
|
// Parameters:
|
|
|
|
|
// - f: The endomorphism to apply for its effect
|
|
|
|
|
//
|
|
|
|
|
// Returns:
|
|
|
|
|
// - A function that takes an endomorphism and chains it with f, keeping the first result
|
|
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
|
|
|
|
// log := func(x int) int { fmt.Println(x); return x }
|
|
|
|
|
// chainLog := endomorphism.ChainFirst(log)
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-12 15:03:55 +01:00
|
|
|
// chained := chainLog(double)
|
|
|
|
|
// result := chained(5) // Prints 10, returns 10
|
|
|
|
|
func ChainFirst[A any](f Endomorphism[A]) Operator[A] {
|
|
|
|
|
return function.Bind2nd(MonadChainFirst, f)
|
2025-11-06 09:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-12 13:51:00 +01:00
|
|
|
// Chain returns a function that chains an endomorphism with another, executing left to right.
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// This is the curried version of MonadChain. It takes an endomorphism f and returns
|
2025-11-12 13:51:00 +01:00
|
|
|
// a function that chains any endomorphism with f, applying the input endomorphism first,
|
|
|
|
|
// then f.
|
|
|
|
|
//
|
|
|
|
|
// IMPORTANT: Execution order is LEFT-TO-RIGHT:
|
|
|
|
|
// - The endomorphism passed to the returned function is applied first
|
|
|
|
|
// - f is applied to the result
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Parameters:
|
2025-11-12 13:51:00 +01:00
|
|
|
// - f: The second endomorphism to apply
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Returns:
|
2025-11-12 13:51:00 +01:00
|
|
|
// - A function that takes an endomorphism and chains it with f (left-to-right)
|
2025-11-06 09:27:00 +01:00
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
|
|
|
|
// increment := func(x int) int { return x + 1 }
|
|
|
|
|
// chainWithIncrement := endomorphism.Chain(increment)
|
2025-11-15 17:00:22 +01:00
|
|
|
// double := N.Mul(2)
|
2025-11-12 13:51:00 +01:00
|
|
|
//
|
|
|
|
|
// // Chains double (first) with increment (second)
|
2025-11-06 09:27:00 +01:00
|
|
|
// chained := chainWithIncrement(double)
|
|
|
|
|
// result := chained(5) // (5 * 2) + 1 = 11
|
2025-11-12 15:03:55 +01:00
|
|
|
func Chain[A any](f Endomorphism[A]) Operator[A] {
|
2025-11-06 09:27:00 +01:00
|
|
|
return function.Bind2nd(MonadChain, f)
|
|
|
|
|
}
|
2025-11-13 12:15:52 +01:00
|
|
|
|
|
|
|
|
// Flatten collapses a nested endomorphism into a single endomorphism.
|
|
|
|
|
//
|
|
|
|
|
// Given an endomorphism that transforms endomorphisms (Endomorphism[Endomorphism[A]]),
|
|
|
|
|
// Flatten produces a simple endomorphism by applying the outer transformation to the
|
|
|
|
|
// identity function. This is the monadic join operation for the Endomorphism monad.
|
|
|
|
|
//
|
|
|
|
|
// The function applies the nested endomorphism to Identity[A] to extract the inner
|
|
|
|
|
// endomorphism, effectively "flattening" the two layers into one.
|
|
|
|
|
//
|
|
|
|
|
// Type Parameters:
|
|
|
|
|
// - A: The type being transformed by the endomorphisms
|
|
|
|
|
//
|
|
|
|
|
// Parameters:
|
|
|
|
|
// - mma: A nested endomorphism that transforms endomorphisms
|
|
|
|
|
//
|
|
|
|
|
// Returns:
|
|
|
|
|
// - An endomorphism that applies the transformation directly to values of type A
|
|
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
|
|
|
|
// type Counter struct {
|
|
|
|
|
// Value int
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // An endomorphism that wraps another endomorphism
|
|
|
|
|
// addThenDouble := func(endo Endomorphism[Counter]) Endomorphism[Counter] {
|
|
|
|
|
// return func(c Counter) Counter {
|
|
|
|
|
// c = endo(c) // Apply the input endomorphism
|
|
|
|
|
// c.Value = c.Value * 2 // Then double
|
|
|
|
|
// return c
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// flattened := Flatten(addThenDouble)
|
|
|
|
|
// result := flattened(Counter{Value: 5}) // Counter{Value: 10}
|
|
|
|
|
func Flatten[A any](mma Endomorphism[Endomorphism[A]]) Endomorphism[A] {
|
|
|
|
|
return mma(function.Identity[A])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Join performs self-application of a function that produces endomorphisms.
|
|
|
|
|
//
|
|
|
|
|
// Given a function that takes a value and returns an endomorphism of that same type,
|
|
|
|
|
// Join creates an endomorphism that applies the value to itself through the function.
|
|
|
|
|
// This operation is also known as the W combinator (warbler) in combinatory logic,
|
|
|
|
|
// or diagonal application.
|
|
|
|
|
//
|
|
|
|
|
// The resulting endomorphism evaluates f(a)(a), applying the same value a to both
|
|
|
|
|
// the function f and the resulting endomorphism.
|
|
|
|
|
//
|
|
|
|
|
// Type Parameters:
|
|
|
|
|
// - A: The type being transformed
|
|
|
|
|
//
|
|
|
|
|
// Parameters:
|
|
|
|
|
// - f: A function that takes a value and returns an endomorphism of that type
|
|
|
|
|
//
|
|
|
|
|
// Returns:
|
|
|
|
|
// - An endomorphism that performs self-application: f(a)(a)
|
|
|
|
|
//
|
|
|
|
|
// Example:
|
|
|
|
|
//
|
|
|
|
|
// type Point struct {
|
|
|
|
|
// X, Y int
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // Create an endomorphism based on the input point
|
|
|
|
|
// scaleBy := func(p Point) Endomorphism[Point] {
|
|
|
|
|
// return func(p2 Point) Point {
|
|
|
|
|
// return Point{
|
|
|
|
|
// X: p2.X * p.X,
|
|
|
|
|
// Y: p2.Y * p.Y,
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// selfScale := Join(scaleBy)
|
|
|
|
|
// result := selfScale(Point{X: 3, Y: 4}) // Point{X: 9, Y: 16}
|
|
|
|
|
func Join[A any](f Kleisli[A]) Endomorphism[A] {
|
|
|
|
|
return func(a A) A {
|
|
|
|
|
return f(a)(a)
|
|
|
|
|
}
|
|
|
|
|
}
|