fp_library/types/
endomorphism.rs

1//! Implementations for [`Endomorphism`], a wrapper for endomorphisms (morphisms from an object to the same object) that enables monoidal operations.
2
3use crate::{
4	Apply,
5	classes::{category::Category, monoid::Monoid, semigroup::Semigroup},
6	kinds::*,
7};
8use std::{
9	fmt::{self, Debug, Formatter},
10	hash::Hash,
11};
12
13/// A wrapper for endomorphisms (morphisms from an object to the same object) that enables monoidal operations.
14///
15/// `Endomorphism c a` represents a morphism `c a a` where `c` is a `Category`.
16/// For the category of functions, this represents functions of type `a -> a`.
17///
18/// It exists to provide a monoid instance where:
19///
20/// * The binary operation [append][Semigroup::append] is [morphism composition][crate::classes::semigroupoid::Semigroupoid::compose].
21/// * The identity element [empty][Monoid::empty] is the [identity morphism][Category::identity].
22///
23/// The wrapped morphism can be accessed directly via the [`.0` field][Endomorphism#structfield.0].
24pub struct Endomorphism<'a, C: Category, A>(pub Apply!(brand: C, signature: ('a, A, A)));
25
26impl<'a, C: Category, A> Endomorphism<'a, C, A> {
27	/// Creates a new `Endomorphism`.
28	///
29	/// # Type Signature
30	///
31	/// `forall a c. Category c => c a a -> Endomorphism c a`
32	///
33	/// # Parameters
34	///
35	/// * `f`: The morphism to wrap.
36	///
37	/// # Returns
38	///
39	/// A new `Endomorphism`.
40	pub fn new(f: Apply!(brand: C, signature: ('a, A, A))) -> Self {
41		Self(f)
42	}
43}
44
45impl<'a, C: Category, A> Clone for Endomorphism<'a, C, A>
46where
47	Apply!(brand: C, signature: ('a, A, A)): Clone,
48{
49	fn clone(&self) -> Self {
50		Self::new(self.0.clone())
51	}
52}
53
54impl<'a, C: Category, A> Debug for Endomorphism<'a, C, A>
55where
56	Apply!(brand: C, signature: ('a, A, A)): Debug,
57{
58	fn fmt(
59		&self,
60		fmt: &mut Formatter<'_>,
61	) -> fmt::Result {
62		fmt.debug_tuple("Endomorphism").field(&self.0).finish()
63	}
64}
65
66impl<'a, C: Category, A> Eq for Endomorphism<'a, C, A> where
67	Apply!(brand: C, signature: ('a, A, A)): Eq
68{
69}
70
71impl<'a, C: Category, A> Hash for Endomorphism<'a, C, A>
72where
73	Apply!(brand: C, signature: ('a, A, A)): Hash,
74{
75	fn hash<H: std::hash::Hasher>(
76		&self,
77		state: &mut H,
78	) {
79		self.0.hash(state);
80	}
81}
82
83impl<'a, C: Category, A> Ord for Endomorphism<'a, C, A>
84where
85	Apply!(brand: C, signature: ('a, A, A)): Ord,
86{
87	fn cmp(
88		&self,
89		other: &Self,
90	) -> std::cmp::Ordering {
91		self.0.cmp(&other.0)
92	}
93}
94
95impl<'a, C: Category, A> PartialEq for Endomorphism<'a, C, A>
96where
97	Apply!(brand: C, signature: ('a, A, A)): PartialEq,
98{
99	fn eq(
100		&self,
101		other: &Self,
102	) -> bool {
103		self.0 == other.0
104	}
105}
106
107impl<'a, C: Category, A> PartialOrd for Endomorphism<'a, C, A>
108where
109	Apply!(brand: C, signature: ('a, A, A)): PartialOrd,
110{
111	fn partial_cmp(
112		&self,
113		other: &Self,
114	) -> Option<std::cmp::Ordering> {
115		self.0.partial_cmp(&other.0)
116	}
117}
118
119impl<'a, C: Category, A: 'a> Semigroup for Endomorphism<'a, C, A> {
120	/// Composes two endomorphisms.
121	///
122	/// # Type Signature
123	///
124	/// `forall a c. Semigroup (Endomorphism c a) => (Endomorphism c a, Endomorphism c a) -> Endomorphism c a`
125	///
126	/// # Parameters
127	///
128	/// * `a`: The second morphism to apply.
129	/// * `b`: The first morphism to apply.
130	///
131	/// # Returns
132	///
133	/// The composed morphism `a . b`.
134	///
135	/// # Examples
136	///
137	/// ```
138	/// use fp_library::types::endomorphism::Endomorphism;
139	/// use fp_library::brands::RcFnBrand;
140	/// use fp_library::classes::clonable_fn::ClonableFn;
141	/// use fp_library::classes::semigroup::Semigroup;
142	///
143	/// let f = Endomorphism::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| x * 2));
144	/// let g = Endomorphism::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| x + 1));
145	/// let h = Semigroup::append(f, g);
146	/// assert_eq!(h.0(5), 12); // (5 + 1) * 2
147	/// ```
148	fn append(
149		a: Self,
150		b: Self,
151	) -> Self {
152		Self::new(C::compose(a.0, b.0))
153	}
154}
155
156impl<'a, C: Category, A: 'a> Monoid for Endomorphism<'a, C, A> {
157	/// Returns the identity endomorphism.
158	///
159	/// # Type Signature
160	///
161	/// `forall a c. Monoid (Endomorphism c a) => () -> Endomorphism c a`
162	///
163	/// # Returns
164	///
165	/// The identity morphism.
166	///
167	/// # Examples
168	///
169	/// ```
170	/// use fp_library::types::endomorphism::Endomorphism;
171	/// use fp_library::brands::RcFnBrand;
172	/// use fp_library::classes::monoid::Monoid;
173	///
174	/// let id = Endomorphism::<RcFnBrand, i32>::empty();
175	/// assert_eq!(id.0(5), 5);
176	/// ```
177	fn empty() -> Self {
178		Self::new(C::identity())
179	}
180}
181
182#[cfg(test)]
183mod tests {
184	use super::*;
185	use crate::{
186		brands::RcFnBrand,
187		classes::{clonable_fn::ClonableFn, monoid::empty, semigroup::append},
188	};
189	use quickcheck_macros::quickcheck;
190
191	// Semigroup Laws
192
193	/// Tests the associativity law for Semigroup.
194	#[quickcheck]
195	fn semigroup_associativity(val: i32) -> bool {
196		let f = Endomorphism::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
197			x.wrapping_add(1)
198		}));
199		let g = Endomorphism::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
200			x.wrapping_mul(2)
201		}));
202		let h = Endomorphism::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
203			x.wrapping_sub(3)
204		}));
205
206		let lhs = append(f.clone(), append(g.clone(), h.clone()));
207		let rhs = append(append(f, g), h);
208
209		lhs.0(val) == rhs.0(val)
210	}
211
212	// Monoid Laws
213
214	/// Tests the left identity law for Monoid.
215	#[quickcheck]
216	fn monoid_left_identity(val: i32) -> bool {
217		let f = Endomorphism::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
218			x.wrapping_add(1)
219		}));
220		let id = empty::<Endomorphism<RcFnBrand, i32>>();
221
222		let res = append(id, f.clone());
223		res.0(val) == f.0(val)
224	}
225
226	/// Tests the right identity law for Monoid.
227	#[quickcheck]
228	fn monoid_right_identity(val: i32) -> bool {
229		let f = Endomorphism::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
230			x.wrapping_add(1)
231		}));
232		let id = empty::<Endomorphism<RcFnBrand, i32>>();
233
234		let res = append(f.clone(), id);
235		res.0(val) == f.0(val)
236	}
237}