Skip to main content

fp_library/types/
send_endofunction.rs

1use crate::{
2	classes::{monoid::Monoid, semigroup::Semigroup, send_cloneable_fn::SendCloneableFn},
3	functions::identity,
4};
5use std::{
6	fmt::{self, Debug, Formatter},
7	hash::Hash,
8};
9
10/// A thread-safe wrapper for endofunctions (functions from a set to the same set) that enables monoidal operations.
11///
12/// `SendEndofunction a` represents a function `a -> a` that is `Send + Sync`.
13///
14/// It exists to provide a monoid instance where:
15///
16/// * The binary operation [append][Semigroup::append] is [function composition][crate::functions::compose].
17/// * The identity element [empty][Monoid::empty] is the [identity function][crate::functions::identity].
18///
19/// The wrapped function can be accessed directly via the [`.0` field][SendEndofunction#structfield.0].
20///
21/// ### Type Parameters
22///
23/// * `FnBrand`: The brand of the thread-safe cloneable function wrapper.
24/// * `A`: The input and output type of the function.
25///
26/// ### Fields
27///
28/// * `0`: The wrapped thread-safe function.
29///
30/// ### Examples
31///
32/// ```
33/// use fp_library::{brands::*, functions::*, types::*};
34///
35/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
36/// assert_eq!(f.0(5), 10);
37/// ```
38pub struct SendEndofunction<'a, FnBrand: SendCloneableFn, A>(
39	pub <FnBrand as SendCloneableFn>::SendOf<'a, A, A>,
40);
41
42impl<'a, FnBrand: SendCloneableFn, A> SendEndofunction<'a, FnBrand, A> {
43	/// Creates a new `SendEndofunction`.
44	///
45	/// This function wraps a thread-safe function `a -> a` in a `SendEndofunction` struct.
46	///
47	/// ### Type Signature
48	///
49	/// `forall fn_brand a. (a -> a) -> SendEndofunction fn_brand a`
50	///
51	/// ### Type Parameters
52	///
53	/// * `FnBrand`: The brand of the function (e.g., `ArcFnBrand`).
54	/// * `A`: The input and output type of the function.
55	///
56	/// ### Parameters
57	///
58	/// * `f`: The function to wrap.
59	///
60	/// ### Returns
61	///
62	/// A new `SendEndofunction`.
63	///
64	/// ### Examples
65	///
66	/// ```
67	/// use fp_library::{brands::*, functions::*, types::*};
68	///
69	/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
70	/// assert_eq!(f.0(5), 10);
71	/// ```
72	pub fn new(f: <FnBrand as SendCloneableFn>::SendOf<'a, A, A>) -> Self {
73		Self(f)
74	}
75}
76
77impl<'a, FnBrand: SendCloneableFn, A> Clone for SendEndofunction<'a, FnBrand, A> {
78	fn clone(&self) -> Self {
79		Self::new(self.0.clone())
80	}
81}
82
83impl<'a, FnBrand: SendCloneableFn, A> Debug for SendEndofunction<'a, FnBrand, A>
84where
85	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Debug,
86{
87	fn fmt(
88		&self,
89		fmt: &mut Formatter<'_>,
90	) -> fmt::Result {
91		fmt.debug_tuple("SendEndofunction").field(&self.0).finish()
92	}
93}
94
95impl<'a, FnBrand: SendCloneableFn, A> Eq for SendEndofunction<'a, FnBrand, A> where
96	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Eq
97{
98}
99
100impl<'a, FnBrand: SendCloneableFn, A> Hash for SendEndofunction<'a, FnBrand, A>
101where
102	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Hash,
103{
104	fn hash<H: std::hash::Hasher>(
105		&self,
106		state: &mut H,
107	) {
108		self.0.hash(state);
109	}
110}
111
112impl<'a, FnBrand: SendCloneableFn, A> Ord for SendEndofunction<'a, FnBrand, A>
113where
114	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Ord,
115{
116	fn cmp(
117		&self,
118		other: &Self,
119	) -> std::cmp::Ordering {
120		self.0.cmp(&other.0)
121	}
122}
123
124impl<'a, FnBrand: SendCloneableFn, A> PartialEq for SendEndofunction<'a, FnBrand, A>
125where
126	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: PartialEq,
127{
128	fn eq(
129		&self,
130		other: &Self,
131	) -> bool {
132		self.0 == other.0
133	}
134}
135
136impl<'a, FnBrand: SendCloneableFn, A> PartialOrd for SendEndofunction<'a, FnBrand, A>
137where
138	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: PartialOrd,
139{
140	fn partial_cmp(
141		&self,
142		other: &Self,
143	) -> Option<std::cmp::Ordering> {
144		self.0.partial_cmp(&other.0)
145	}
146}
147
148impl<'a, FnBrand: 'a + SendCloneableFn, A: 'a + Send + Sync> Semigroup
149	for SendEndofunction<'a, FnBrand, A>
150{
151	/// The result of combining the two values using the semigroup operation.
152	///
153	/// This method combines two endofunctions into a single endofunction.
154	/// Note that `SendEndofunction` composition is reversed relative to standard function composition:
155	/// `append(f, g)` results in `f . g` (read as "f after g"), meaning `g` is applied first, then `f`.
156	///
157	/// ### Type Signature
158	///
159	/// `forall fn_brand a. Semigroup (SendEndofunction fn_brand a) => (SendEndofunction fn_brand a, SendEndofunction fn_brand a) -> SendEndofunction fn_brand a`
160	///
161	/// ### Parameters
162	///
163	/// * `a`: The second function to apply (the outer function).
164	/// * `b`: The first function to apply (the inner function).
165	///
166	/// ### Returns
167	///
168	/// The composed function `a . b`.
169	///
170	/// ### Examples
171	///
172	/// ```
173	/// use fp_library::{brands::*, functions::*, types::*};
174	///
175	/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
176	/// let g = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x + 1));
177	///
178	/// // f(g(x)) = (x + 1) * 2
179	/// let h = append::<_>(f, g);
180	/// assert_eq!(h.0(5), 12);
181	/// ```
182	fn append(
183		a: Self,
184		b: Self,
185	) -> Self {
186		let f = a.0;
187		let g = b.0;
188		// Compose: f . g
189		Self::new(<FnBrand as SendCloneableFn>::send_cloneable_fn_new(move |x| f(g(x))))
190	}
191}
192
193impl<'a, FnBrand: 'a + SendCloneableFn, A: 'a + Send + Sync> Monoid
194	for SendEndofunction<'a, FnBrand, A>
195{
196	/// The identity element.
197	///
198	/// This method returns the identity endofunction, which wraps the identity function.
199	///
200	/// ### Type Signature
201	///
202	/// `forall fn_brand a. Monoid (SendEndofunction fn_brand a) => () -> SendEndofunction fn_brand a`
203	///
204	/// ### Returns
205	///
206	/// The identity endofunction.
207	///
208	/// ### Examples
209	///
210	/// ```
211	/// use fp_library::{brands::*, functions::*, types::*};
212	///
213	/// let id = empty::<SendEndofunction<ArcFnBrand, i32>>();
214	/// assert_eq!(id.0(5), 5);
215	/// ```
216	fn empty() -> Self {
217		Self::new(<FnBrand as SendCloneableFn>::send_cloneable_fn_new(identity))
218	}
219}